我有一些这样的文件:
??? abc.conf
??? def.conf
??? xyz.conf
??? 10-abc.conf.postix
??? 10-def.conf.postix
Run Code Online (Sandbox Code Playgroud)
我想.postfix
从所有以10-
. 为了在不安装任何工具的情况下执行此操作,我尝试使用bash
,find
和来执行此操作sed
。
我建立了这个:
$ find . -name "10-*.conf.postfix" -exec mv '{}' $(echo {} | sed -E 's/(.*)\.postfix$/\1/') \;
Run Code Online (Sandbox Code Playgroud)
但它失败了,因为mv
-command 将呈现为:
$ mv '10-abc.conf.postix' 10-abc.conf.postix
Run Code Online (Sandbox Code Playgroud)
如果我在我的子 shell 中测试代码,那么它会按预期工作(它返回不带 的文件名.postifx
)。我不确定出了什么问题。你对我有什么暗示吗?
子shell,就像变量一样,在整个命令行运行之前被处理。该$()
是由家长shell来解释,而不是通过“查找”,并没有什么特殊待遇给予“发现-exec”为“发现”本身并不是一个特殊的外壳构造,但只有一个普通的外部命令。
所以处理顺序是这样的:
echo {} | sed -E 's/(.*)\.postfix$/\1/'
导致输出{}
。find . -name "10-*.conf.postfix" -exec mv '{}' {} \;
运行。mv
作为子进程执行,用找到的文件名替换每个 {} 参数。有几种选择可以做你想做的事:
引用子shell,使其不会被父shell扩展(使用单引号或反斜杠转义$
),然后要求'find'通过shell运行它(否则它根本不会被扩展):
find . -name "10-*.conf.postfix" -exec sh -c 'mv "$1" "$(echo "$1" | sed -E "s/.../")"' -- {} \;
Run Code Online (Sandbox Code Playgroud)
这可以使用 缩短sed 's/\.postfix$//'
,因为除了剥离后缀之外,您实际上并没有做任何其他事情。但是,Bash 的参数扩展会使用${var%suffix}
以下命令使其更短:
find . -name "10-*.conf.postfix" -exec bash -c 'mv "$1" "${1%.postfix}"' -- {} \;
Run Code Online (Sandbox Code Playgroud)
使用perl-rename
(在 Debian 上称为“重命名”)或其他可以直接转换源名称而无需子 shell 的工具:
find . -name "10-*.conf.postfix" -exec prename 's/\.postfix$//' {} \;
Run Code Online (Sandbox Code Playgroud)
如果你不需要包含子目录,你可以简单地运行:
prename 's/\.postfix$//' 10-*.conf.postfix
Run Code Online (Sandbox Code Playgroud)
使用for
循环,这是一个 shell 构造,其中主体的处理被延迟:
for file in ./10-*.conf.postfix; do
# mv "$file" "$(echo "$file" | sed -E 's/(.*)\.postfix$/\1/')"
# mv "$file" "$(echo "$file" | sed 's/\.postfix$//')"
mv "$file" "${file%.postfix}"
done
Run Code Online (Sandbox Code Playgroud)
使用 bash 时,您可以启用shopt -s globstar
并指定**/*.conf.postfix
递归匹配文件。只要匹配的数量不是很大,这是一个很好的替代find -name
.
(注意:避免使用,for x in $(find)
除非您确切地知道它将扩展到什么。)
使用vidir
或其他交互式“目录编辑”工具。