I am having files like a_dbg.txt, b_dbg.txt ...
in a Suse 10
system. I want to write a bash shell script which should rename these files by removing "_dbg" from them.
Google suggested me to use rename
command. So I executed the command rename _dbg.txt .txt *dbg*
on the CURRENT_FOLDER
My actual CURRENT_FOLDER
contains the below files.
CURRENT_FOLDER/a_dbg.txt
CURRENT_FOLDER/b_dbg.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt
After executing the rename
command,
CURRENT_FOLDER/a.txt
CURRENT_FOLDER/b.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt
Its not doing recursively, how to make this command to rename files in all subdirectories. Like XX
and YY
I will be having so many subdirectories which name is unpredictable. And also my CURRENT_FOLDER
will be having some other files also.
You can use find
to find all matching files recursively:
$ find . -iname "*dbg*" -exec rename _dbg.txt .txt '{}' \;
EDIT: what the '{}'
and \;
are?
The -exec
argument makes find execute rename
for every matching file found. '{}'
will be replaced with the path name of the file. The last token, \;
is there only to mark the end of the exec expression.
All that is described nicely in the man page for find:
-exec utility [argument ...] ;
True if the program named utility returns a zero value as its
exit status. Optional arguments may be passed to the utility.
The expression must be terminated by a semicolon (``;''). If you
invoke find from a shell you may need to quote the semicolon if
the shell would otherwise treat it as a control operator. If the
string ``{}'' appears anywhere in the utility name or the argu-
ments it is replaced by the pathname of the current file.
Utility will be executed from the directory from which find was
executed. Utility and arguments are not subject to the further
expansion of shell patterns and constructs.
small script i wrote to replace all files with .txt extension to .cpp extension under /tmp and sub directories recursively
#!/bin/bash
for file in $(find /tmp -name '*.txt')
do
mv $file $(echo "$file" | sed -r 's|.txt|.cpp|g')
done
For renaming recursively I use the following commands:
find -iname \*.* | rename -v "s/ /-/g"
with bash:
shopt -s globstar nullglob
rename _dbg.txt .txt **/*dbg*
Scipt above can be written in one line:
find /tmp -name "*.txt" -exec bash -c 'mv $0 $(echo "$0" | sed -r \"s|.txt|.cpp|g\")' '{}' \;
find -execdir rename
to rename files and directories with a regular expression
If you are going to rename both files and directories with regular expressions, and not simply with a suffix, then this is a good pattern:
find-rename-regex() (
set -eu
find_and_replace="$1"
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find . -depth -execdir rename "${2:--n}" "s/${find_and_replace}" '{}' \;
)
Sample usage to replace spaces ' ' with hyphens '-'. Dry run:
find-rename-regex ' /-/g'
Do the replace:
find-rename-regex ' /-/g' -v
The awesome -execdir
option does a cd
into the directory before executing the rename
command, unlike -exec
.
-depth
ensure that the renaming happens first on children, and then on parents, to prevent potential problems with missing parent directories.
-execdir
is required because rename does not play well with non-basename input paths, e.g. the following fails:
rename 's/findme/replaceme/g' acc/acc
The PATH
hacking is required because -execdir
has one very annoying drawback: find
is extremely opinionated and refuses to do anything with -execdir
if you have any relative paths in your PATH
environment variable, e.g. ./node_modules/.bin
, failing with:
find: The relative path ‘./node_modules/.bin’ is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH
-execdir
is a GNU find extension to POSIX. rename
is Perl based and comes from the rename
package.
Rename lookahead workaround
If your input paths don't come from find
, or if you've had enough of the relative path annoyance, we can use some Perl lookahead to safely rename directories as in:
git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'
I haven't found a convenient analogue for -execdir
with xargs
: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686
The sort -r
is required to ensure that files come after their respective directories, since longer paths come after shorter ones with the same prefix.
Tested in Ubuntu 18.10.
If you just want to rename and don't mind using an external tool, then you can use rnm. The command would be:
#on current folder
rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' *
-dp -1
will make it recursive to all subdirectories.
-fo
implies file only mode.
-ssf '_dbg'
searches for files with _dbg in the filename.
-rs '/_dbg//'
replaces _dbg with empty string.
You can run the above command with the path of the CURRENT_FOLDER too:
rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' /path/to/the/directory
You can use this below.
rename --no-act 's/\.html$/\.php/' *.html */*.html
classic solution:
for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done
来源:https://stackoverflow.com/questions/16541582/find-multiple-files-and-rename-them-in-linux