我在a_dbg.txt, b_dbg.txt ...
系统中有像Suse 10
这样的文件。我想编写一个bash shell脚本,它应该通过从中删除“_dbg”来重命名这些文件。
谷歌建议我使用rename
命令。所以我在rename _dbg.txt .txt *dbg*
上执行了命令CURRENT_FOLDER
我的实际CURRENT_FOLDER
包含以下文件。
CURRENT_FOLDER/a_dbg.txt
CURRENT_FOLDER/b_dbg.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt
执行rename
命令后,
CURRENT_FOLDER/a.txt
CURRENT_FOLDER/b.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt
它没有递归,如何使这个命令重命名所有子目录中的文件。像XX
和YY
一样,我将拥有许多名称不可预测的子目录。而且我的CURRENT_FOLDER
也会有一些其他文件。
您可以使用find
递归查找所有匹配的文件:
$ find . -iname "*dbg*" -exec rename _dbg.txt .txt '{}' \;
编辑:'{}'
和\;
是什么?
-exec
参数使find为找到的每个匹配文件执行rename
。 '{}'
将替换为文件的路径名。最后一个标记\;
只用于标记exec表达式的结尾。
所有这些都在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.
我编写的小脚本用递归方式替换.txt扩展名为/ tmp和子目录下的.cpp扩展名的所有文件
#!/bin/bash
for file in $(find /tmp -name '*.txt')
do
mv $file $(echo "$file" | sed -r 's|.txt|.cpp|g')
done
为了递归重命名,我使用以下命令:
find -iname \*.* | rename -v "s/ /-/g"
用bash:
shopt -s globstar nullglob
rename _dbg.txt .txt **/*dbg*
上面的脚本可以写成一行:
find /tmp -name "*.txt" -exec bash -c 'mv $0 $(echo "$0" | sed -r \"s|.txt|.cpp|g\")' '{}' \;
find -execdir rename
用正则表达式重命名文件和目录
如果要使用正则表达式重命名文件和目录,而不是简单地使用后缀,那么这是一个很好的模式:
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}" '{}' \;
)
用连字符''替换空格''的示例用法。干运行:
find-rename-regex ' /-/g'
做替换:
find-rename-regex ' /-/g' -v
与-execdir
不同,令人敬畏的cd
选项在执行rename
命令之前将-exec
放入目录。
-depth
确保重命名首先发生在子项上,然后发生在父项上,以防止丢失父目录的潜在问题。
-execdir
是必需的,因为重命名不能很好地与非基本名称输入路径一起使用,例如:以下失败:
rename 's/findme/replaceme/g' acc/acc
PATH
黑客攻击是必需的,因为-execdir
有一个非常恼人的缺点:如果你的find
环境变量中有任何相对路径,-execdir
非常自以为是并拒绝对PATH
做任何事情,例如: ./node_modules/.bin
,失败者:
find:相对路径'./node_modules/.bin'包含在PATH环境变量中,与find的-execdir操作结合使用是不安全的。请从$ PATH中删除该条目
-execdir
是一个GNU找到extension to POSIX。 rename
是基于Perl的,来自rename
包。
重命名预测变通方法
如果您的输入路径不是来自find
,或者您已经有足够的相对路径烦恼,我们可以使用一些Perl前瞻来安全地重命名目录,如下所示:
git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'
我没有找到一个方便的-execdir
与xargs
类似:https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686
sort -r
需要确保文件位于各自的目录之后,因为较长的路径位于具有相同前缀的较短路径之后。
在Ubuntu 18.10中测试过。
如果您只想重命名并且不介意使用外部工具,那么您可以使用rnm。命令是:
#on current folder
rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' *
-dp -1
将使其递归到所有子目录。
-fo
意味着仅文件模式。
-ssf '_dbg'
在文件名中搜索带有_dbg的文件。
-rs '/_dbg//'
用空字符串替换_dbg。
您也可以使用CURRENT_FOLDER的路径运行上述命令:
rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' /path/to/the/directory
你可以在下面使用它。
rename --no-act 's/\.html$/\.php/' *.html */*.html
经典方案:
for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done