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 .
Another method could be to use
(cd ../../; pwd)
If this were executed in any top-level paths such as /
, /usr/
, or /usr/share/
, you would get a valid directory of /
, but when you get one level deeper, you would start seeing results: /usr/share/man/
would return /usr
, /my/super/deep/path/is/awesome/
would return /my/super/deep/path
, and so on.
You could store this in a variable as well:
GRANDDADDY="$(cd ../../; pwd)"
and then use it for the rest of your script.
Assuming filepath doesn't end in /
, which it shouldn't if you use dirname
, you can do
Parent = "${filepath%/*}"
Grandparent = "${filepath%/*/*}"
So do something like this
[[ "${filepath%/*/*}" == "" ]] && echo "Path isn't long enough" || echo "${filepath%/*/*}"
Also this likely won't work if you're using relative paths (like find .
). In which case you will want to use
filepath=$(dirname "$f")
filepath=$(readlink -f "$filepath")
instead of
filepath=$(dirname "$f")
Also you're never stripping the extension, so there is no reason to get it from the file and then append it again.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With