-1

EDIT: Thanks for the help on this. I was hoping to run it as a for loop so I could pass the extension in as an argument later eg: for file in (find . -type f -name '*.'$1); do etc... Could anyone add the best approach to adapting one of the solutions to work in the loop context after my do?

Original post:

I'm embarrassed at how long I've been working at this one.

I'm trying to append filename with last 3 characters of the file's parent directory in Bash.

For example, my directories look like this:

192.168.1.101
192.168.1.102 etc...

and the files within each directory look something like this:

120000.JPG
120137.JPG

and I would like to rename the files to look like so:

101_120000.JPG

I've tried everthing from sed, to awk and haven't quite got there yet. I'm trying to use a shell script with a for loop. I've managed to print the last 3 of the parent directory using:

for f in `find . -name '*.'$1`
do 
echo $(dirname $f) | (tail -c 4 $t)

but I haven't been able to pass that off as a variable to use in the mv command in the for loop. If I try:

for f in `find . -name '*.'$1`
do
lastThree=$(dirname $f) | (tail -c 4 $t)

I get an error "tail: error reading './192.168.1.101': Is a directory"

Curious if I'm approaching the solution the right way. If anyone has suggestions on how to solve, I'd be much appreciated.

  • Neither of the answers actually answers the question you asked, which was how to rename the file per your example; however Léa Gris's answer can be adapted to work, e.g. `find ./ -type f -exec bash -c 'f="${0##*/}"; d="${0%/*}"; echo mv -v "$d/$f" "$d/${d: -3}_$f"' {} \;` will print out the `mv` _command_ that would be executed if the `echo` command was not there. If you like the output, run it again without the `echo` _command_ to rename the files. Note however this will rename any file found, not just the pattern e.g. `120000.JPG` because neither answer limits the output to that pattern. – user3439894 Dec 03 '19 at 06:45
  • You can place `mv -v` between `echo` and `"$1"` in the sergio's answer for it to actual rename the files after the `echo` _command_ is removed after testing its output. The last sentence of my previous comment applies here as well. Also note that the `-v` _option_ on the `mv` command isn't necessary, just a personal preference. – user3439894 Dec 03 '19 at 06:51
  • Thanks! I was hoping to run it as a for loop so i could pass the extension in as an argument later. as in `for file in (find . -type f -name '*.'$1); do` etc... How would I adapt the answer to work in the loop context after my do? – camwrangles Dec 03 '19 at 07:24

2 Answers2

2

Execute an inline bash script with the find command like this:

find ./ -type f -exec bash -c 'f="${0##*/}"; d="${0%/*}"; echo "${d: -3}_$f"' {} \;

Output of find is passed as argument 0 to the script.

Variable f is the filename without directory path obtained by trimming anything leading last / with f="${0##*/}"

Variable d is the directory path without the file name. It is obtained by trimming trailing characters starting at last /: d="${0%/*}".

Then echo "${d: -3}_$f" prints the last 3 characters of the directory name, an underscore _ and the file name.

Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • My god, what witchcraft is this?! It looks like your cat walked on your keyboard, but it works! I'm new to scripting so this is magic to me. Thanks for taking the time to respond. As you mentioned, this prints the last 3 characters of the directory name, an underscore _ and the file name, but I'm not sure how to execute this as a rename. Would I need to add a mv command to the script? – camwrangles Dec 03 '19 at 05:20
  • 1
    It's called *parameter expansion* and this answer should not have been downvoted. (though there could have been explanation why the mysterious space was needed before `-3`) See [man bash](http://man7.org/linux/man-pages/man1/bash.1.html) under heading `"EXPANSION"`, subheading `"Parameter Expansion"`. – David C. Rankin Dec 03 '19 at 05:29
  • 1
    Hey I didn't downvote. I tried upvoting, but I got no street cred with the new account. Thank you both for the responses. I still don't know how to actuate all this good advice into a rename. Forgive my naiveté, but still figuring out the basics. It dosen't seem to actually rename files, but rather print what the renamed files should look like without actually changing them. Any pointers would be helpful. – camwrangles Dec 03 '19 at 05:42
-1

You could use:

find . -type f -exec bash -c 'echo "$1" "$(sed "s:\([0-9a-zA-Z]*\)/\([^/]*\)$:\1/\1_\2:g" <<<$1)"' -- {} \;

the file name substitution is made with sed:

sed "s:\([0-9a-zA-Z]*\)/\([^/]*\)$:\1/\1_\2:g" <<<$1

The pattern, :\([0-9a-zA-Z]*\)/\([^/]*\)$:, matches zero or more alphanumeric characters followed by a slash followed by zero or more nonslash follow by the end of the string ($). The replacement is :\1/\1_\2: which back references the first and second captured groups.

builder-7000
  • 7,131
  • 3
  • 19
  • 43