I have a number of directories with titles such as "20150512_101129_3016" and others with additional string following an additional underscore, such as "20150512_101129_3016_v1A" (it's not always v1A, could be v1, or V1a, etc). I would like to rename all directories such that the final string following the final underscore is removed. I'd prefer a solution that looks to remove all text following (and including) the final/3rd underscore since I am uncertain that all directory names will have the same number of characters in the text preceding the 3rd underscore.
2 Answers
You can do this with a simple shell loop:
for x in ./*_*_*_*; do mv -i "$x" "${x%_*}"; done
i.e. for every file whose name contains at least three underscores, rename the file, stripping off the part starting at the last underscore.
If there's a collision (e.g. both 20150512_101129_3016_v1A
and 20150512_101129_3016_v1B
exist), then the first file in lexicographic order is renamed to the desired target, and the second one is moved to a subdirectory, i.e. 20150512_101129_3016_v1B
gets moved to 20150512_101129_3016/20150512_101129_3016_v1B
after 20150512_101129_3016_v1A
has been renamed to 20150512_101129_3016
. To avoid this, add an extra check:
for x in ./*_*_*_*; do
if [ -e "${x%_*}" ]; then
echo "Not renaming $x because ${x%_*} already exists"
continue
fi
mv "$x" "${x%_*}"
done
Alternatively, on Linux, pass the -T
option to mv
to make it do this check.

- 829,060
if the original directory names are homogeneous, i.e. follows the format of XXXXX_XXXX_XXXX (then may be followed by another _ XXXX) this code segment below, should work:
find ./ -maxdepth 1 -type d | while read dir; do
newdir=$(echo ${dir} | cut -d_ -f1-3)
# duplicate directory name protection
if [ -d ${newdir} ]
then
newdir=${newdir}_
echo "duplicate directory encountered. Appending _ to the new dir name"
fi
#did the dir name change ? If not, do nothing
if [ ${newdir} != ${dir} ]
then
mv ${dir} ${newdir}
fi
done
it assumes you are in the directory which contains these subdirectories.

- 6,966
-
1If this is a full tree then your
find
needs a-depth
flag. You should also test to see if$dir
and$newdir
are different. Also test to see if$newdir
already exists (eg ifa_b_c_v1
anda_b_c_v1a
both exist they'll both try to becomea_b_c
). – Stephen Harris Aug 15 '16 at 19:08 -
I tried running the most recently edited suggested code in a .sh file and returned:
./suffix.sh: line 11: unexpected EOF while looking for matching }' ./suffix.sh: line 16: syntax error: unexpected end of file
– focusbob Aug 15 '16 at 19:30 -
I added a matching } to "newdir" on line 11, then returned: `find: warning: you have specified the -maxdepth option after a non-option argument -type, but options are not positional (-maxdepth affects tests specified before it as well as those specified after it). Please specify options before other arguments.
duplicate directory encountered. Appending _ to the new dir name mv: cannot move
– focusbob Aug 15 '16 at 19:32./' to
./_': Device or resource busy` -
I apologize a
}
sign went victim to an accidental backspace. Fixed it in the code. Please understand that, the improved code will add an underscore if you happen to have the same directory name with and without a 3rd _ character. I.e., if you have20150512_101129_3016
and20150512_101129_3016_V1a
, the latter will become20150512_101129_3016_
to protect the original – MelBurslan Aug 15 '16 at 19:43 -
Thank you. 2 more questions: 1) if I leave in the duplicate directory protection, it appears to also append "" if there is no 3rd underscore, for example
20150512_101129_3016
becomes `20150512_101129_3016. Is there a way to prevent this from happening (as it is not a duplicate)? 2) if I remove the protection, it appears to run fine, but returns:
mv:./' and
./.' are the same file. Also, if I have a directory name without a 3rd underscore as per above, I get
mv: cannot move./20150213_123852_3005' to a subdirectory of itself,
./20150213_123852_3005/20150213_123852_3005'` – focusbob Aug 15 '16 at 20:10 -
You can remove the first
if
block if you are sure there will be no duplicate names during processing. 2ndif
block is needed to prevent messages likemv: ./' and ./.' are the same file
. It doesn't affect the effective outcome but throws out error messages like this, for directories which doesn't have the 3rd _ character in the name. But the second if block doesn't append the _ character by any means – MelBurslan Aug 15 '16 at 20:42 -
Always use double quotes around variable substitutions. And don't use external tools for simple string operations that can be performed in the shell: it's hard to read, difficult to get right, and slow. – Gilles 'SO- stop being evil' Aug 15 '16 at 23:06
"${file%_*}"
– jasonwryan Aug 15 '16 at 19:07