Replace underscore with minus recursively but only for certain extensions? - rename

I want to replace all underscores with a minus for files in a folder and its subfolders and I want to include only jpg, JPG and png. For a start I tried rename '/_/-/g' ./* but that does not include subfolders. How would I do that?
Thanks

Globstar allows you to match files anywhere in a folder or under it. ${f//_/-} is a bash "Pattern substitution": it replaces the value of $f, the double slashes before the pattern mean replace all matches.
shopt -s globstar
for f in **/*.jpg **/*.JPG **/*.png
do
mv "$f" "${f//_/-}"
done
My system does not support this syntax of rename, so I can't give you good advice in its use, but you might also be able to use globstar to generate filenames for it.

On Linux, you can traverse the folders with "find":
find . -name "*.pdf" -type f -exec #rename command here, current filename is "{}"# \;
Edit: Forgot the \;

Related

Find all files with specific name and move it two levels up from its location

I want to find all files with specific name "stdout.1.0", move it two/three levels up from its location. While moving it two/three levels up, I also want to rename it to "testjob.out".
All "stdout.1.0" files are located six levels down from parent directory.
./dirXXXXXX/dirXXXXXX/dirXXXXXX/dirXXXXXX/dirXXXXXX/dirXXXXXX/stdout.1.0
I used:
find . -type f -name stdout.1.0
and it outputs:
./dir100000/dir110000/dir111000/dir111100/dir111110/dir111111/stdout.1.0
./dir100000/dir110000/dir112000/dir111100/dir111110/dir111111/stdout.1.0
./dir100000/dir110000/dir113000/dir111100/dir111110/dir111111/stdout.1.0
./dir200000/dir210000/dir211000/dir211100/dir211110/dir211111/stdout.1.0
./dir200000/dir210000/dir212000/dir211100/dir211110/dir211111/stdout.1.0
./dir200000/dir210000/dir213000/dir211100/dir211110/dir211111/stdout.1.0
./dir300000/dir310000/dir311000/dir311100/dir311110/dir311111/stdout.1.0
./dir300000/dir310000/dir312000/dir311100/dir311110/dir311111/stdout.1.0
./dir300000/dir310000/dir313000/dir311100/dir311110/dir311111/stdout.1.0
.
.
./dirXXX000/dirXXX000/dirXXX000/dirXXX100/dirXXX110/dirXXX111/stdout.1.0
The directories above is just representative of where the file is, but there are multiple "stdout.1.0" files starting three levels down from parent directory.
Here is a method in plain bash using globstar shell option , without using the find:
#!/bin/bash
shopt -s globstar
for file in **/stdout.1.0; do
echo mv "$file" "${file%/*/*/*}/testjob.out"
done
Drop the echo if output looks fine.
You already know how to find them:
find . -type f -name stdout.1.0
Now, you need to move them to a higher directory (..) and rename them:
find . -type f -name stdout.1.0 -execdir mv {} ../../testjob.out \;
I would advise you to copy them first and remove later (use cp instead of mv): if anything goes wrong, you can get back easily to the current situation.

find recursively, but with specific sub-folder name

This command find all files name "log_7" recursively in current folder.
find . -name log_7
Assume many sub-folders under the current folder tree has a file with that same name "log_7":
./am/f1/log_7
./ke/f2/log_7
./sa/f6/log_7
..
./xx/f97/log_7
Is there a way to explicitly say that we only want to search for "log_7" in a folder name "f2" ? such that the result from find will only list only one entry:
./ke/f2/log_7
You could use a regular expression.
find . -regex '.*/f2/log_7'
This will only match if log_7 is directly nested under f2
There is a different way is to use xargs for same thing
find . -name filename | xargs grep -e refectory
But find with built in regex is preferable.
Simple glob should do:
printf '%s\n' */f2/log_7
If there is a possibility for more leading folders, you can use the globstar option:
shopt -s globstar
printf '%s\n' **/f2/log_7

Scan For Multiple File Exentions and Copy To New Dir If Found

I am using Ubuntu and I am needing to recursively search a directory and subs for any .mp4, .mkv, or .wav files. I saw that mmv may be my best solution, but I can not get the syntax correct. So how would I convert this to search for an array of file names, or would I just want to run 3 iterations for each file extension I am after?
I was using the tutorial here to write this, so please forgive me if I am way outta line
find all m3
# find / -iname "*.mp4" -type f -exec mv {} ./Downloads/Media Files \;
With GNU bash 4:
shopt -s globstar nullglob
mv -v **/*.{mp4,mkv,wav} ./Downloads/Media Files
globstar: If set, the pattern ** used in a pathname expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a /, only directories and subdirectories match.
nullglob: If set, bash allows patterns which match no files (see Pathname Expansion) to expand to a null string, rather than themselves.
You can use \( and -o (or):
find / -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.wav" \) -exec mv {} ./Downloads/Media Files \;
Replace -iname with -regex. Regex understands emacs regular expressions by default (but you can change this behaviour using -regextype):
find / -regex ".*\.mp4\|.*\.mkv\|.*\.wav" ...
Learn the power of reguar expressions, it will open a new universe of power!

how do i find all files with the same name in all subdirectories

I want to find all the different .gitignore files I have to combine them into one.
I tried something like find */**/.gitignore but that came up with nothing.
All the files are in sub directories of my current dir, as well as the current directory.
I am using Bash on linux
find -name .gitignore
That should do
Since you are using bash:
shopt -s globstar
echo **/.gitignore
From man bash:
globstar
If set, the pattern ** used in a pathname expansion context will match a
files and zero or more directories and subdirectories. If the pattern is
followed by a /, only directories and subdirectories match.
Try this
find /home/user1 -name 'result.out' 2>/dev/null
In your case
find /<your DIR> -name '*.gitignore' 2>/dev/null
This results in
/home/user1/result.out
/home/user1/dir1/result.out
/home/user1/dir2/result.out

Add prefix to all images (recursive)

I have a folder with more than 5000 images, all with JPG extension.
What i want to do, is to add recursively the "thumb_" prefix to all images.
I found a similar question: Rename Files and Directories (Add Prefix) but i only want to add the prefix to files with the JPG extension.
One of possibly solutions:
find . -name '*.jpg' -printf "'%p' '%h/thumb_%f'\n" | xargs -n2 echo mv
Principe: find all needed files, and prepare arguments for the standard mv command.
Notes:
arguments for the mv are surrounded by ' for allowing spaces in filenames.
The drawback is: this will not works with filenames what are containing ' apostrophe itself, like many mp3 files. If you need moving more strange filenames check bellow.
the above command is for dry run (only shows the mv commands with args). For real work remove the echo pretending mv.
ANY filename renaming. In the shell you need a delimiter. The problem is, than the filename (stored in a shell variable) usually can contain the delimiter itself, so:
mv $file $newfile #will fail, if the filename contains space, TAB or newline
mv "$file" "$newfile" #will fail, if the any of the filenames contains "
the correct solution are either:
prepare a filename with a proper escaping
use a scripting language what easuly understands ANY filename
Preparing the correct escaping in bash is possible with it's internal printf and %q formatting directive = print quoted. But this solution is long and boring.
IMHO, the easiest way is using perl and zero padded print0, like next.
find . -name \*.jpg -print0 | perl -MFile::Basename -0nle 'rename $_, dirname($_)."/thumb_".basename($_)'
The above using perl's power to mungle the filenames and finally renames the files.
Beware of filenames with spaces in (the for ... in ... expression trips over those), and be aware that the result of a find . ... will always start with ./ (and hence try to give you names like thumb_./file.JPG which isn't quite correct).
This is therefore not a trivial thing to get right under all circumstances. The expression I've found to work correctly (with spaces, subdirs and all that) is:
find . -iname \*.JPG -exec bash -c 'mv "$1" "`echo $1 | sed \"s/\(.*\)\//\1\/thumb/\"`"' -- '{}' \;
Even that can fall foul of certain names (with quotes in) ...
In OS X 10.8.5, find does not have the -printf option. The port that contained rename seemed to depend upon a WebkitGTK development package that was taking hours to install.
This one line, recursive file rename script worked for me:
find . -iname "*.jpg" -print | while read name; do cur_dir=$(dirname "$name"); cur_file=$(basename "$name"); mv "$name" "$cur_dir/thumb_$cur_file"; done
I was actually renaming CakePHP view files with an 'admin_' prefix, to move them all to an admin section.
You can use that same answer, just use *.jpg, instead of just *.
for file in *.JPG; do mv $file thumb_$file; done
if it's multiple directory levels under the current one:
for file in $(find . -name '*.JPG'); do mv $file $(dirname $file)/thumb_$(basename $file); done
proof:
jcomeau#intrepid:/tmp$ mkdir test test/a test/a/b test/a/b/c
jcomeau#intrepid:/tmp$ touch test/a/A.JPG test/a/b/B.JPG test/a/b/c/C.JPG
jcomeau#intrepid:/tmp$ cd test
jcomeau#intrepid:/tmp/test$ for file in $(find . -name '*.JPG'); do mv $file $(dirname $file)/thumb_$(basename $file); done
jcomeau#intrepid:/tmp/test$ find .
.
./a
./a/b
./a/b/thumb_B.JPG
./a/b/c
./a/b/c/thumb_C.JPG
./a/thumb_A.JPG
jcomeau#intrepid:/tmp/test$
Use rename for this:
rename 's/(\w{1})\.JPG$/thumb_$1\.JPG/' `find . -type f -name *.JPG`
For only jpg files in current folder
for f in `ls *.jpg` ; do mv "$f" "PRE_$f" ; done

Resources