-2

I have subfolders with dashes (example subdir with name c-d). I need to rename all .jpg files with all subdirs, any tips how to do it?

I've tried many option, but nothing helped (I think because my subfolders contains dashes -)

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
  • 4
    Show us what you have done so far please so we can help you – Julien Rousé Feb 06 '18 at 13:21
  • 3
    What is your pattern for renaming files? What are original names and what do you want to get? – Michael Feb 06 '18 at 13:23
  • I'm tried find -name '*.jpg' -exec rename $RANDOM.jpg $RANDOM.jpg {} \; but getting (Missing operator before jpg?) syntax error at (user-supplied code) line 3, near "2635.jpg" Bareword found where operator expected at (eval 4) line 1, near "#line 1 2635.jpg" – Jane Farrow Feb 06 '18 at 13:45
  • What do you mean "rename all .jpg files with all subdirs"? If the file is `./a-b/c-d/e.jpg` what should the new name be? – glenn jackman Feb 06 '18 at 14:06
  • New name can be whatever name. It can be just random [0-9].jpg. I have folder A with subfolders a-a b-b c-c. Each subfolder contains .jpg image. I want rename all this .jpg in all subfolders – Jane Farrow Feb 06 '18 at 14:08
  • In example this script can rename only in current folder for i in *.jpg; do mv $i $RANDOM.jpg; done But how i can rename .jpg files in all subfolders? – Jane Farrow Feb 06 '18 at 14:09
  • There is absolutely no guarantee that your generated filenames will be unique. You may lose data. Also, this is really unclear and arbitrary; is it a homework assignment? How about an example of what you've tried? Try reading over [this](https://stackoverflow.com/help/asking) first. – Paul Hodges Feb 06 '18 at 14:24

1 Answers1

0

$RANDOM can get you collisions. Let's use the md5 checksum hashes instead since they're random-looking yet determined by the content (meaning the only collisions will be when you have identical files anyway).

while IFS= read -r -d '' file; do
  mv "$file" "${file%/*}/$( md5sum "$file" |awk '{print $1}' )".jpg
done < <(find . -name '*.jpg' -print0)

This takes the md5 checksum of each file's contents and renames the file to it, preserving the directory structure and a trailing .jpg

This looks a bit ugly due to needing to preserve spaces.

Running while IFS= loops over contents with no input field separator ($IFS). Each iteration calls read -r -d '' file, which reads in content delimited by a null character while ignoring backslash escapes and stores entries in a variable called $file.

Skipping to the end of the loop, you can see it iterating on the null-terminated output of a find command that looks for files named *.jpg. This is performed via Bash process substitution in order to prevent scoping issues with a subshell created by a standard pipe (more detail can be found in BashFAQ/020, which I adapted for this problem).

Inside the loop, we have the actual rename action, which uses command substitution to run md5sum on the file and then clean it up with awk (which is told to print only the first field, which is the checksum). The directory structure is preserved by parameter expansion, where ${file%/*} strips the last slash and all trailing non-slash characters so we can append the hash and then .jpg to the new file name. (This is the same as $(dirname "$file") but without the system call.)

If you really want random numbers and you don't care about collisions, replace $( md5sum "$file" |awk '{print $1}' ) with $RANDOM. If you give mv the -i option, it will prompt you in the event of a collision in the same directory.

This will not work in POSIX shells or other basic shells due to nonstandard flags in read and the process substitution. You can probably use mkfifo and/or xargs -0 to work around that, but that's not going to be easy.

Adam Katz
  • 14,455
  • 5
  • 68
  • 83