Subshel​​l 不能像缩进那样工作

ako*_*kop 4 bash sed find

我有一些这样的文件:

??? 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)。我不确定出了什么问题。你对我有什么暗示吗?

use*_*686 9

子shell,就像变量一样,在整个命令行运行之前被处理。$()是由家长shell来解释,而不是通过“查找”,并没有什么特殊待遇给予“发现-exec”为“发现”本身并不是一个特殊的外壳构造,但只有一个普通的外部命令。

所以处理顺序是这样的:

  1. 子shellecho {} | sed -E 's/(.*)\.postfix$/\1/'导致输出{}
  2. 命令find . -name "10-*.conf.postfix" -exec mv '{}' {} \;运行。
  3. 当“find”找到匹配的文件时,它会直接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或其他交互式“目录编辑”工具。