user1051532
user1051532

Reputation: 129

Bash, run script on every .jpg file in a directory with subdirectories

I have some working code, it's very simple - it copies every *.jpg file, renames it into *1.jpg, no worries.

for i in *.jpg; do cp $i ${i%%.jpg}1.jpg;  done

how can I run this, so that it works on every file in a directory, every file in the subdirectories of that directory

example directory structure:

    test/test1/test2/somefile.jpg
    test/anotherfile.jpg
    test/test1/file.jpg
etc

Upvotes: 5

Views: 5563

Answers (2)

doubleDown
doubleDown

Reputation: 8398

If you have Bash 4, you can use globstar to enable recursive filename expansion (i.e. matches files in subdirectories as well)

shopt -s globstar
for i in **/*.jpg; do cp "$i" "${i%%.jpg}1.jpg";  done

p/s: I added quotes around the arguments for cp in case there are spaces in filenames or whatnot.

Upvotes: 2

Brian Campbell
Brian Campbell

Reputation: 332816

The command to do anything recursively to a directory structure is find:

find . -name "*.jpg" -exec bash -c 'file="{}"; cp "$file" "${file%%.jpg}1.jpg"' \;

Using -exec instead of for i in $(find ...) will handle filenames that contain a space. Of course, there is still one quoting gotcha; if the filename contains a ", the file="{}" will expand to file="name containing "quote characters"", which is obviously broken (file will become name containing quote and it will try to execute the characters command).

If you have such filenames, or you might, then print out each filename separated with null characters (which are not allowed in filenames) using -print0, and use while read -d $'\0' i to loop over the null-delimited results:

find . -name "*.jpg" -print0 | \
    (while read -d $'\0' i; do cp "$i" "${i%%.jpg}1.jpg";  done)

As with any complex command like this, it's a good idea to test it without executing anything to make sure it's expanding to something sensible before running it. The best way to do this is to prepend the actual command with echo, so instead of running it, you will see the commands that it will run:

find . -name "*.jpg" -print0 | \
    (while read -d $'\0' i; do echo cp "$i" "${i%%.jpg}1.jpg";  done)

Once you've eyeballed it and the results looks good, remove the echo and run it again to run it for real.

Upvotes: 6

Related Questions