Find files with spaces in the bash - bash

I want to search for files in a folder which have a space in its filenames, f.e.
/vol1/apache2/application/current/Test 1.pdf
/vol1/apache2/application/current/Test 2.pdf
I know there's a find command but I can't figure out the correct parameters to list all those files.

Use find command with a space between two wildcards. It will match files with single or multiple spaces. "find ." will find all files in current folder and all the sub-folders. "-type f" will only look for files and not folders.
find . -type f -name "* *"
EDIT
To replace the spaces with underscores, try this
find . -type f -name "* *" | while read file; do mv "$file" ${file// /_}; done

With find:
find "/vol1/apache2/application/current" -type f -name "*[[:space:]]*"

The following worked for me to find all files containing spaces:
find ./ -type f | grep " "
To also rename all found files to the same filename without the spaces, run the following:
find ./ -type f | grep " " | while read file; do mv "$file" ${file// }; done
Ways to configure the above script:
To rename only directories, change -type f to -type d
To use git-aware rename, change do mv to do git mv
To rename differently, change ${file// }; to ${file// /[some-string]};. For example, to replace the spaces with "_", use: ${file// /_}; (notice the leading "/")

Related

In bash, how can I recursively rename each file to the name of its parent folder, retaining the original extension?

I have a large directory of folders, each of which has only one file:
directory/folder1/208hasdfasdf.jpg
directory/folder2/f230fsdf.gif
directory/folder3/23fsdbfasf.jpg
I'd like to rename this to:
directory2/folder1/folder1.jpg
directory2/folder2/folder2.gif
directory3/folder3/folder3.jpg
How can I do that?
For the path and filenames shown, you can use a loop and combination of find and sed to make the substitutions, e.g.
for f in $(find directory -type f -wholename "*folder*"); do
mv "$f" $(sed -E 's|^([^/]+)/([^/]+)/([^.]+)[.](.*)$|\1/\2/\2.\4|' <<< "$f")
done
Where sed -E 's|^([^/]+)/([^/]+)/([^.]+)[.](.*)$|\1/\2/\2.\4| uses the alternative delimiter '|' instead of '/' to ease dealing with pathnames, and then separates and captures the "directory" with ^([^/]+) and then the "folderX" with ([^/]+), followed by the filename without the extension ([^.]+) and lastly the extension (.*)$ making each component available through the numbered backreferences \1, \2, \3, and \4, respectively.
Then to form the new filename, you just duplicate the \2 foldername in place of the \3 filename, for a new filename of \1/\2/\2.\4
Example Use/Output
$ find tmp-david -type f -wholename "*folder*"
tmp-david/folder3/23fsdbfasf.jpg
tmp-david/folder2/f230fsdf.gif
tmp-david/folder1/208hasdfasdf.jpg
And the replacement of the filenames with
$ for f in $(find tmp-david -type f -wholename "*folder*"); do
> mv "$f" $(sed -E 's|^([^/]+)/([^/]+)/([^.]+)[.](.*)$|\1/\2/\2.\4|' <<< "$f")
> done
Resulting in:
$ find tmp-david -type f -wholename "*folder*"
tmp-david/folder3/folder3.jpg
tmp-david/folder2/folder2.gif
tmp-david/folder1/folder1.jpg
You could try something like this, assuming you're using bash:
find directory/ \( -name '*.gif' -o -name '*.jpg' \) -print |
while read old; do
parent=${old%/*}
base=${parent##*/}
ext=${old##*.}
mv $old $parent/$base.$ext
done
If you're dealing with filenames that contain whitespace you're going
to need to massage this a bit.
Before running this script:
$ find directory -type f -print
directory/folder2/f230fsdf.gif
directory/folder1/208hasdfasdf.jpg
directory/folder3/23fsdbfasf.jpg
After running this script:
$ find directory -type f -print
directory/folder2/folder2.gif
directory/folder1/folder1.jpg
directory/folder3/folder3.jpg

Recursively rename files to remove dots but leave extension unchanged

I've got a number of folders (80?) with files in them. Some of the filenames have multiple dots (file.name.ext). With the 'find' command being recursive, I'm able to rename the filenames within the folders from uppercase to lowercase:
find . -type f -execdir rename 'y/A-Z/a-z/' {} \;
Where 'find .' indicates to search the current folder. Where 'type f' searches only for files. Where 'execdir' executes the subsequent command on the output.
To do the same (uppercase to lowercase), but for the folders, both of these work:
rename 'y/A-Z/a-z/' *
rename 'y/A-Z/a-z/' ./*
To remove the dots from the foldername, this works:
find . -maxdepth 1 -execdir sed 's/[.]/_/g' {} \;
edit:(actually this is not working for me now)
...
What fails is when I try to recursively remove the dots:
find . -type f -execdir rename 's/\.(?=[^.]*\.)/_/g' '{}' \;
I get the error:
Can't rename ./filename.ext _/filename.ext: No such file or directory
I've also tried to add -printf "%f\n" to remove the leading ./ :
find . -type f -printf "%f\n" -execdir rename 's|[.]|_|g; s|_([^_]*)$|.$1|' {} \;
which outputs the filename followed by the same error
file.name.ext
Can't rename ./file.name.ext _/file.name.ext: No such file or directory
Those commands above were run from the parent folder 'above' the 80 folders that contain the files, with the idea of doing a dryrun (rename -nono) on all the files within the 80 folders at once.
From within one of those 80 folders I can remove the dots from the filename, leaving the dot in the extension unchanged, with:
rename 's/\.(?=[^.]*\.)/_/g'
But I don't want to have to go into each of those many folders to run the command. What will work recursively to delete all dots, leaving the extension dot alone?
I found the answer here:
Linux recursively replace periods for all directorys and all but last period for files with underscores
At first I didn't think it answered my specific question, but it actually does.
while IFS= read -rd '' entry; do
entry="${entry#./}" # strip ./
if [[ -d $entry ]]; then
rename 'y/A-Z/a-z/; s/ /_/g; s/\./_/g' "$entry"
else
rename 'y/A-Z/a-z/; s/ /_/g; s/\.(?=.*\.)/_/g' "$entry"
fi
done < <(find . -iname '*' -print0)
Thanks to anubhava .

Rename all files in directory and subdirectory

How do I rename files in directory and subdirectory?
I found this program, but I need to go change files in subdirectory.
for file in *#me01
do
mv "$file" "${file/#me01/_me01}"
done
n#me01
to
n_me01
The following one-liner will likely work for you:
find . -type f -name '*#me01' -execdir rename '#me01' '_me01' {} \;
The following form is likely more correct as it will change only the last # to _ if there are multiple occurrences of #me01 in the file:
for f0 in $(find . -type f -name '*#me01')
do
f1=$(printf '%s' "$f0" | sed 's/#me01$/_me01/')
mv "$f0" "$f1"
done
This latter form is also more flexible and can be built upon more easily as the regex language in sed is much more powerful than rename expressions.
If rename of directories is also required the following can easily be added...
Either:
find . -type d -name '*#me01' -execdir rename '#me01' '_me01' {} \;
Or:
for d0 in $(find . -type d -name '*#me01')
do
d1=$(printf '%s' "$d0" | sed 's/#me01$/_me01/')
mv "$d0" "$d1"
done
Using bash:
shopt -s globstar
for name in **/*#me01; do
mv "$name" "${name%#me01}_me01"
done
This enables the globstar shell option in bash which makes ** match across path separators in pathnames.
It also uses a standard parameter substitution to delete the #me01 portion at the very end of the found pathname and replace it with _me01.

Rename files in several subdirectories

I want to rename a file present in several subdirectories using bash script.
my files are in folders:
./FolderA/ABCD/ABCD_Something.ctl
./FolderA/EFGH/EFGH_Something.ctl
./FolderA/WXYZ/WXYZ_Something.ctl
I want to rename all of the .ctl file with the same name (name.ctl).
I tried several command using mv or rename but didnt work.
Working from FolderA:
find . -name '*.ctl' -exec rename *.ctl name.ctl '{}' \;
or
for f in ./*/*.ctl; do mv "$f" "${f/*.ctl/name .ctl}"; done
or
for f in $(find . -type f -name '*.ctl'); do mv $f $(echo "$f" | sed 's/*.ctl/name.ctl/'); done
Can you help me using bash?
thanks
You can do this with one line with:
find . -name *.ctl -exec sh -c 'mv "$1" `dirname "$1"`/name.ctl' x {} \;
The x just allows the filename to be positional character 1 rather than 0 which (in my opinion) wrong to use as a parameter.
Try this:
find . -name '*.ctl' | while read f; do
dn=$(dirname "${f}")
# remove the echo after you sanity check the output
echo mv "${f}" "${dn}/name.ctl"
done
find should get all the files you want, dirname will get just the directory name, and mv will perform the rename. You can remove the quotes if you're sure that you'll never have spaces in the names.

Rename all files in subfolders - replace string in filename

I want to rename all files in a folder and its subfolders.
I need to change the string HEX20 to the string HEX8.
Some filenames have other numbers, so I cannot simply change the 20 to an 8.
An example of the full path is:
\\FRDS01006\z188018\FEM\Linear\HEX20\3HEX20\3HEX20.bof
I would like to do the same replacement for the folder names.
How about this:
find . -name "*HEX20*" -exec rename HEX20 HEX8 '{}' +
This will search recursively through the current directory and any subdirectories to match HEX20. (The flag -type f is omitted because the asker wants to change the names of directories in addition to files.) It will then build a long rename command and ultimately call it. This type of construction may be simpler than building a series of commands with sed and then executing them one-by-one.
Try this:
find . -type f -name "*HEX20*" | sed 's/\(.*\)HEX20\(.*\)/mv \0 \1HEX8\2/' | sh
This way you find for regular files having HEX20 in their names:
find . -type f -name "*HEX20*"
then change the last occurrence of HEX20 whith HEX8 and compile the mv command:
find . -type f -name "*HEX20*" | sed 's/\(.*\)HEX20\(.*\)/mv \0 \1HEX8\2/'
finally you execute the compiled commands with sh:
find . -type f -name "*HEX20*" | sed 's/\(.*\)HEX20\(.*\)/mv \0 \1HEX8\2/' | sh

Resources