Linux Glob Pattern to remove all files containing a '?'? - linux

I would like to descend into a directory and recursively remove all filenames that contain a ?.
I wgeted a website and files of the form index.html?p=46 were downloaded..extra marks for why this was.
I tried:
rm -R *?*
that failed: removed all regular files
rm -R *\?*
also failed: No such file of directory

Try this: find . -iname '*\?*' -exec rm {} \;
$ ls
xxy x?y
$find . -iname '*\?*'
./x?y
$ find . -iname '*\?*' -exec rm {} \;
$ ls
xxy
As for why it happened, the website you wgetted had links to index.html passing those parameters and you (presumably) told wget to mirror it.

? maps to single character. You need to escape it:
$ touch a a?a
$ ls *?*
a a?a
$ ls *\?*
a?a

Related

Bash command to flatten nested dicrectory

I have a directory structure like this
a/1/01.jpg
b/2/01.jpg
c/3/01.jpg
I want to make it into a structure like this
a1/01.jpg
b2/01.jpg
c3/01.jpg
So far I have a bash command that looks like this
find . -mindepth 2 -type d -execdir bash -c 'mv -i \"$1\" ./\"${1//\/\[/_[}\"' bash {} \;
However the command failed with these statements
mv: cannot stat '"./3"': No such file or directory
mv: cannot stat '"./2"': No such file or directory
mv: cannot stat '"./1"': No such file or directory
What am I doing wrong here? is there a better way to do this?
Doing that by using plain bash would be easier:
#!/bin/bash
for src in */*/; do
dst=${src/\/}
echo mkdir "$dst"
echo mv "$src"* "$dst"
done
Remove both echos if the output looks fine.
Or, a more efficient version:
for src in */*/; do
mv "$src" "${src/\/}"
done
but this version won't work properly when the destination directory (a1, b2, c3, etc.) already exists.
All operations need to be performed on the leaf directories. These are identified by having two links which you can find with:
$ find -type d -links 2
Once you have these directories, you only have to rename the directory
$ find -type d -links 2 -exec sh -c 'd1={};d2="${d1#./}"; [ "$d2" = "${d2//\//}" ] || mv -v "$d1" "${d2//\//}"' \;
Find will complain a bit as you moved a directory away that it was traversing, but this should do it.
If you have a pure structure, i.e. files only appear in leaf-directories, this should do it. All you need to do now is delete the empty directories:
$ find -type d -empty -delete
Be aware, however, that if a leaf directory was already empty, the latter will remove it.

how to tell find command to only remove contents of a directory

I am using find to get both files and dirs inside $dest_dir and remove them:
dest_dir="$HOME/pics"
# dest_dir content:
# dir1
# dir2
# pic1
# pic2
find $dest_dir -maxdepth 1 -exec rm -rf {} \;
Expectation: remove dest_dir contents only (i. e. dir1, dir2, pic1, pic2) and not dest_dir itself
Actual result: the command removes the dest_dir too
I also tried -delete instead of the -exec rm -rf {} \; section, but it can't remove non-empty directories.
If you pass a directory name to rm -rf it will delete it, by definition. If you don't want to recurse into subdirectories, why are you using find at all?
rm "$dest_dir"/*
On the other hand, if you want to rm -rf everything inside the directory ... Do that instead.
rm -rf "$dest_dir"/*
On the third hand, if you do want to remove files, but not directories, from an arbitrarily deep directory tree, try
find "$dest_dir" -type f -delete
or somewhat more obscurely with -execdir and find just the directories and pass in a command like sh -c 'rm {}/*', but in this scenario this is just clumsy and complex.
You can use this find command:
find "$dest_dir" -maxdepth 1 -mindepth 1 -exec rm -rf {} +
Option -mindepth 1 will find all entries inside "$dest_dir" at least one level down and will skip "$dest_dir" itself.

Newbie-ish error: cp: omitting directory

Pulling my hair as I'm stuck with a basic error without understanding why:
find . -type f -exec cp del {} \;
We're in a "test" directory, in which I created one "del" subdirectory. The "test" directory contains a variety of files of various types.
The result is a series of lines (same number as the number of files present in the directory from where the command is ran) with:
cp: omitting directory `del'
Possibly useful details follow.
Debian Wheezy, standard shell interface.
As a prelude to more complex exclusion and exec patterns I wanted to start with this fundamental test... and had this.
I think I excluded the "del" directory with "type -f", so it's not as if I was asking Linux to move a directory within itself.
There are no other directories or subdirectories.
Permissions: everything belongs to the current user.
I made variations for the "cp del" part, putting it in simple or double quotes, or using ./del, no difference.
I also tried with -R
find . -type f -name '*script1*' -exec cp -R ./del {} \;
That gave:
cp: cannot overwrite non-directory `./script1' with directory `./del'
Same with -r
If what you're trying to do is to copy some files found by find command to the del directory, then you can do it like this:
find . -type f | xargs cp -T del/
Or like this:
find . -type f -exec cp {} del \;

Create file in Linux and replace content

I have a project in Linux. I want to create a file named index.html in all folders.
So I have used the following command:
find . -type d -exec touch {}/index.html \;
It's working! Now I'm trying to copy the existing file from a given location and it to be automatically replaced into all the folders of my project.
This should actually work exactly in the same way:
find . -type d -exec cp $sourcedir/index.html {}/index.html \;
If I understand your question correctly, what you want is to copy a given file in all the directories.
You can use a similar find command :
find . -type d -exec cp -f /tmp/index.html {} \;
where /tmp/index.html is path to the original file (replace it with your own path).
Also, you don't need to create the files if your final objective is to replace them with the original file.
tar -cvzf index.tar.gz `find . -type f -iname 'index.html'` && scp index.tar.gz USER#SERVER:/your/projec/root/on/SERVER && ssh USER#SERVER "tar -xvzf index.tar.gz"
Or if you're in the proper directory localhost, and rsync is available:
rsync -r --exclude='**' --include='**/index.html' . USER#SERVER:/your/projec/root/on/SERVER
HTH

How to remove all .svn directories from my application directories

One of the missions of an export tool I have in my application, is to clean all .svn directories from my application directory tree. I am looking for a recursive command in the Linux shell that will traverse the entire tree and delete the .svn files.
I am not using export, as this script will be used for some other file/directory names which are not related to SVN. I tried something like:
find . -name .svn | rm -fr
It didn't work...
Try this:
find . -name .svn -exec rm -rf '{}' \;
Before running a command like that, I often like to run this first:
find . -name .svn -exec ls '{}' \;
What you wrote sends a list of newline separated file names (and paths) to rm, but rm doesn't know what to do with that input. It's only expecting command line parameters.
xargs takes input, usually separated by newlines, and places them on the command line, so adding xargs makes what you had work:
find . -name .svn | xargs rm -fr
xargs is intelligent enough that it will only pass as many arguments to rm as it can accept. Thus, if you had a million files, it might run rm 1,000,000/65,000 times (if your shell could accept 65,002 arguments on the command line {65k files + 1 for rm + 1 for -fr}).
As persons have adeptly pointed out, the following also work:
find . -name .svn -exec rm -rf {} \;
find . -depth -name .svn -exec rm -fr {} \;
find . -type d -name .svn -print0|xargs -0 rm -rf
The first two -exec forms both call rm for each folder being deleted, so if you had 1,000,000 folders, rm would be invoked 1,000,000 times. This is certainly less than ideal. Newer implementations of rm allow you to conclude the command with a + indicating that rm will accept as many arguments as possible:
find . -name .svn -exec rm -rf {} +
The last find/xargs version uses print0, which makes find generate output that uses \0 as a terminator rather than a newline. Since POSIX systems allow any character but \0 in the filename, this is truly the safest way to make sure that the arguments are correctly passed to rm or the application being executed.
In addition, there's a -execdir that will execute rm from the directory in which the file was found, rather than at the base directory and a -depth that will start depth first.
No need for pipes, xargs, exec, or anything:
find . -name .svn -delete
Edit: Just kidding, evidently -delete calls unlinkat() under the hood, so it behaves like unlink or rmdir and will refuse to operate on directories containing files.
There are already many answers provided for deleting the .svn-directory. But I want to add, that you can avoid these directories from the beginning, if you do an export instead of a checkout:
svn export <url>
If you don't like to see a lot of
find: `./.svn': No such file or directory
warnings, then use the -depth switch:
find . -depth -name .svn -exec rm -fr {} \;
In Windows, you can use the following registry script to add "Delete SVN Folders" to your right click context menu. Run it on any directory containing those pesky files.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN]
#="Delete SVN Folders"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN\command]
#="cmd.exe /c \"TITLE Removing SVN Folders in %1 && COLOR 9A && FOR /r \"%1\" %%f IN (.svn) DO RD /s /q \"%%f\" \""
You almost had it. If you want to pass the output of a command as parameters to another one, you'll need to use xargs. Adding -print0 makes sure the script can handle paths with whitespace:
find . -type d -name .svn -print0|xargs -0 rm -rf
find . -name .svn |xargs rm -rf
As an important issue, when you want to utilize shell to delete .svn folders You need -depth argument to prevent find command entering the directory that was just deleted and showing error messages like e.g.
"find: ./.svn: No such file or directory"
As a result, You can use find command like below:
cd [dir_to_delete_svn_folders]
find . -depth -name .svn -exec rm -fr {} \;
Try this:
find . -name .svn -exec rm -v {} \;
Read more about the find command at developerWorks.
Alternatively, if you want to export a copy without modifying the working copy, you can use rsync:
rsync -a --exclude .svn path/to/working/copy path/to/export

Resources