user1201155 user1201155 - 4 years ago 175
Linux Question

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

: No such file or directory". This is running on CentOS 5.6.

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


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

export -f dirnametofilename

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

find .

Answer Source

* 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.


  • 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.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download