【问题标题】:How to substitute string in {} in "find (...) -exec (...) {} \;" bash command?如何在“find (...) -exec (...) {} \;”中替换 {} 中的字符串bash 命令?
【发布时间】:2018-10-28 14:53:04
【问题描述】:

如何替换 bash 的“find”找到的 {} 中的字符串?

例如,我希望下面的? 将“in”替换为“out”:

find . -name "*.in" -exec python somescript.py {} ? \;

即对所有“*.in”文件执行

python somescript.py somefile.in somefile.out

【问题讨论】:

  • 如果有意义的话,你也可以修改 somescript.py。

标签: bash find


【解决方案1】:

或者,只需修改 somescript.py 以创建输出文件名(在 Python 中比在 shell 中更容易,因为没有可能以某种方式解释的特殊字符):

find . -name '*.in' -exec python somescript.py {} .out \;

随着脚本的开始:

import sys, os
inputfilename = sys.argv[1]
outputfilename = os.path.splitext(inputfilename)[0] + sys.argv[2]

# ...

当然,您也可以硬编码 .out 并删除该参数。

如果您由于某种原因无法修改 somescript.py,您可以轻松创建一个包装脚本,负责以正确的方式调用 somescript.py

【讨论】:

    【解决方案2】:

    对我来说,所有内置工具使用起来都有些麻烦(我的 shell 引用正确吗?它会做正确的事吗?我可以轻松预览更改吗?)。

    几年前,我写了rpt,它将接受它的命令行参数,打开一个文本编辑器(让你编辑文件名),然后在旧的和新的上运行一个命令(默认mv)文件名。

    最近,我更进一步,这里是fea(对于每个参数):

    #!/usr/bin/env python3
    # fea: For Each Argument
    # https://thp.io/2019/rpt-and-fea.html
    
    import sys, shlex, re
    
    _, cmd, *args = sys.argv
    
    print('\n'.join(re.sub(r'[{](.)([^}]*?)(\1)([^}]*?)(\1)[}]',
        lambda m: shlex.quote(re.sub(m.group(2), m.group(4), arg)),
        cmd).replace('{}', shlex.quote(arg)) for arg in args))
    

    会发生以下替换:

    {} -> insert arg
    {/regex/replacement/} -> run re.sub(regex, replacement) on the arg,
                             you can pick any character for "/", as long
                             as it appears at the start, middle and end
                             (to separate the regex from the replacement)
    

    一些用例:

    # Create backup files
    fea "cp {} {}.bak" *.py
    
    # Encode WAV files with oggenc
    fea "oggenc {} -o {/wav$/ogg/}" *.wav
    
    # Decode MP3 files with mpg123
    fea "mpg123 -w {/mp3$/wav/} {}" *.mp3
    
    # Render markdown documents to HTML
    fea "markdown {} > {/md$/html/}" *.md
    
    # Fancy replacement
    fea "markdown {} > {#input/(.*).md#output/\1.html#}" input/*.md
    
    # As mentioned above, note that you need to pipe the
    # fea output into a shell to execute the command
    fea "cp {} {}.bak" *.py | sh -e -x
    

    您的用例可以这样覆盖:

    find . -name '*.in' -print0 | xargs -0 fea "python somescript.py {} {/in$/out/}"
    

    或者如果文件只是在当前文件夹中:

    fea "python somescript.py {} {/in$/out/}" *.in
    

    或者,如果您将zshrecursive globbing 一起使用:

    fea "python somescript.py {} {/in$/out/}" **/*.in
    

    如果您对它显示的命令感到满意,只需将其输出通过管道传输到sh -e -x(或bash 或其他)以执行它们。

    虽然可以使用 shell(或任何参数/变量扩展)创建 for 循环,但 shell 引用和转义很难正确(对我来说),fea 工具可以确保它也适用于奇怪的名字:

    touch 'some$weird "filename.py'
    touch 'and !! more.py'
    touch 'why oh why?.py'
    touch "this is ridiculous'.py"
    fea "cp {} {}.bak" *.py | sh -e -x
    

    详情请见rpt and fea blog post

    【讨论】:

      【解决方案3】:

      使用Brace Expansion

      find . -name "*.in" -exec bash -c 'python script.py "${0%.*}"{.in,.out}' {} \;
      

      使用Shell Parameter Expansion

      find . -name "*.in" -exec bash -c 'python script.py "${0} ${0/.in/.out}"' {} \;
      

      结果

      python script.py somefile.in somefile.out
      

      【讨论】:

        【解决方案4】:

        bash -c 命令与find 命令一起使用:

        find -name "*.in" -exec bash -c 'python somescript.py "$1" "$(dirname "$1")/$(basename "$1" .in).out"' _ {} \;
        

        找到的文件名{} 被赋予bash 作为$1 参数。

        命令basename 删除扩展名,dirname 保留路径名。

        【讨论】:

        • 这里basename 不起作用:它删除了目录部分,但目录部分很重要。
        • "${1/%.in/.out}"或更短的"${1%.in}.out"
        • @NahuelFouilleul 不,这会改变,例如large.inputs/foo.inlarge.outputs/foo.in。要去除后缀,请使用${1%.in}
        • @Gilles 感谢您的反馈,但%/ 之后确保模式在最后,但后来我意识到不需要更换
        【解决方案5】:

        find 没有替换功能。你需要调用一个shell。

        find . -name "*.in" -exec sh -c 'python somescript.py "$0" "${0%.in}.out"' {} \;
        

        $0 是文件名,${0%.in} 去掉了.in 后缀。

        或者,在 bash(但不是普通 sh)中,运行 shopt -s globstar 以启用递归目录扩展(如果存在没有任何匹配的 .in 文件的风险,则运行 shopt -s nullglob)并使用 @ 987654329@ 循环而不是 find

        for x in **/*.in; do
          python somescript.py "$x" "${x%.in}.out"
        done
        

        【讨论】:

        • 也可能是shopt -s nullglob[[ -e $x ]]||continue
        • 我怎样才能用替换来写呢?我尝试了“${0/in/out},但我收到了“错误替换”错误。
        • @jul sh 中没有替换。在 bash 中,这将运行而不会出现错误,但通常不会产生正确的结果(例如,它会将 large.inputs/foo.in 变成 large.outputs/foo.in)。
        • 好的,这适用于bash。正如您所评论的,我只需要注意要替换的模式多次出现的情况。
        猜你喜欢
        • 1970-01-01
        • 2013-12-07
        • 1970-01-01
        • 1970-01-01
        • 2021-08-12
        • 2023-01-23
        • 2021-07-16
        • 1970-01-01
        • 2020-09-02
        相关资源
        最近更新 更多