Search code examples
linuxbashpathcentosbatch-rename

Get grandparent directory in bash script - rename files for a directory in their paths


I have the following script, which I normally use when I get a bunch of files that need to be renamed to the directory name which contains them.

The problem now is I need to rename the file to the directory two levels up. How can I get the grandparent directory to make this work?

With the following I get errors like this example: "mv: cannot move ./48711/zoom/zoom.jpg to ./48711/zoom/./48711/zoom.jpg: No such file or directory". This is running on CentOS 5.6.

I want the final file to be named: 48711.jpg

#!/bin/bash

function dirnametofilename() {
  for f in $*; do
    bn=$(basename "$f")
    ext="${bn##*.}"
    filepath=$(dirname "$f")
    dirname=$(basename "$filepath")
    mv "$f" "$filepath/$dirname.$ext"
  done
}

export -f dirnametofilename

find . -name "*.jpg" -exec bash -c 'dirnametofilename "{}"'  \;

find .

Solution

  • Note:
    * This answer solves the OP's specific problem, in whose context "grandparent directory" means: the parent directory of the directory containing a file (it is the grandparent path from the file's perspective).
    * By contrast, given the question's generic title, other answers here focus (only) on getting a directory's grandparent directory; the succinct answer to the generic question is: grandParentDir=$(cd ../..; printf %s "$PWD") to get the full path, and grandParentDirName=$(cd ../..; basename -- "$PWD") to get the dir. name only.

    Try the following:

    find . -name '*.jpg' \
      -execdir bash -c \
       'old="$1"; new="$(cd ..; basename -- "$PWD").${old##*.}"; echo mv "$old" "$new"' - {} \;
    

    Note: echo was prepended to mv to be safe - remove it to perform the actual renaming.

    • -execdir ..\; executes the specified command in the specific directory that contains a given matching file and expands {} to the filename of each.

    • bash -c is used to execute a small ad-hoc script:

      • $(cd ..; basename -- "$PWD") determines the parent directory name of the directory containing the file, which is the grandparent path from the file's perspective.

      • ${old##*.} is a Bash parameter expansion that returns the input filename's suffix (extension).

      • Note how {} - the filename at hand - is passed as the 2nd argument to the command in order to bind to $1, because bash -c uses the 1st one to set $0 (which is set to dummy value _ here).

    • Note that each file is merely renamed, i.e., it stays in its original directory.

    Caveat:

    • Each directory with a matching file should only contain 1 matching file, otherwise multiple files will be renamed to the same target name in sequence - effectively, only the last file renamed will survive.