1

I have a bunch of folders I need to rename on a Linux disk.

The folders are named as below

VT_GH

I would like to rename every single folder called VT_GH to VT GH

I only want to remand folders with that exact name. Essentially replacing the underscore with a space in that folder name recursively on the system. There are about 50,000 of these folders.

Its on RHEL7 and this can be run as root.

JandP
  • 31

2 Answers2

0

Looks a bit complicated, but that should do the trick:

find / -depth -type d -name VT_GH -print | awk '{ print "mv -n -T \"" $0 "\" \"`dirname '"'"'" $0 "'"'"'`/VT GH\"" }' | bash

Let's break it into pieces. The first command:

find / -depth -type d -name VT_GH -print

outputs a list of pathnames of all folders called VT_GH on your system. The -depth parameter is important here, as it causes the paths to be listed in order from deepest ones. This will be important later when we rename them, because if we have a path like /some/dir/VT_GH/VT_GH, then the lower-level VT_GH must be renamed before the upper-level one, otherwise the path to the lower-level one will cease to exist.

The second command:

awk '{ print "mv -n -T \"" $0 "\" \"`dirname '"'"'" $0 "'"'"'`/VT GH\"" }'

transforms this list into a list of mv commands, where each command has the form:

mv -n -T "/a path/to/some/random dir/VT_GH" "`dirname '/a path/to/some/random dir/VT_GH'`/VT GH"

The first argument of each mv command is the path to source directory, enclosed in double quotes to protect from possible spaces in directory names (as in "a path" and "random dir").

The second argument consists of two parts. The first part is output of the dirname command called on the same path as above (this time the path is enclosed in single quotes, as we need the double quotes to quote the entire second argument). So for /a path/to/some/random dir/VT_GH the dirname command will output just /a path/to/some/random dir. Then we add /VT GH to it - the new name we want - and enclose everything in double quotes, again to protect from spaces.

The -n -T parameters to mv command tell it to not do anything if there is already folder called VT GH along with VT_GH in the same directory. In that case VT_GH will remain unchanged.

This list of mv commands is just provided as input to bash, which is a final part of this one-liner :)

raj
  • 2,245
0

If your find supports -execdir (GNU find does) then this is quite simple:

find . -depth -type d -name VT_GH ! -execdir test -e 'VT GH' \; -execdir mv -i {} 'VT GH' \; -print

If your find does not support -execdir then you need it to run a shell (or shells) to manipulate paths. A POSIX solution may be:

find . -depth -type d -name VT_GH -exec sh -c '
   for f do
      ( cd "${f%/*}" && ! test -e "VT GH" && mv -i VT_GH "VT GH" && printf "%s\n" "$f" )
   done
' find-sh {} +

Notes:

  • The starting point is . for testing (I mean you can create a test directory and test inside it). You want a solution that works "recursively on the system", so eventually use / instead of . in the code. Still it's good to exclude at least /proc, /dev and /sys. You can explicitly exclude them or give find starting points that are more specific than / (-xdev may be useful).
  • -depth is needed to prevent find from entering a path that doesn't exist anymore (compare this answer; rm or mv, the same issue).
  • The code tests if there is no VT GH in the way. Still this is prone to TOCTOU. If VT GH appears between test and mv then the code will misbehave. If the new VT GH is a directory or a symlink to a directory then mv will move VT_GH to the inside of VT GH (where another VT_GH may already exist, therefore mv -i). If the new VT GH is not a directory then it will not be overwritten (this is true even if you agree to overwrite when mv -i prompts you; you will get cannot overwrite non-directory with directory from mv). Consider mv -n -T (if supported, the options are not portable).
  • In general tests and actions performed by find are prone to TOCTOU. E.g. it's possible a directory VT_GH passes -type d but when mv is executed a regular file VT_GH is there instead.
  • -print (or printf in the shell) is only there to inform you what directories were altered.
  • find-sh is explained here: What is the second sh in sh -c 'some shell code' sh?
  • If test and printf are builtins in your sh then the second form may actually be faster. The first form does not spawn sh but it spawns one test process per VT_GH directory. The second form spawns possibly multiple sh processes but each sh process handles many directories and saves you many test processes.