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