【问题标题】:Unix Shell Loop through files and replace textsUnix Shell 循环文件并替换文本
【发布时间】:2015-05-19 08:24:48
【问题描述】:

我对这些东西还很陌生,我需要一个 shell 文件来遍历文件夹中的所有“.xml”文件,并进行一些文本替换。 到目前为止,我想出了这个:

sed "s/old_text/new_text/g" testfile.xml -i

但是,我希望它在当前文件夹中的所有 xml 文件上运行,而不仅仅是在“testfile.xml”上。另外,如何备份原文件?

欢迎任何意见! 非常感谢!

【问题讨论】:

    标签: shell loops unix sed


    【解决方案1】:

    要对所有 xml 文件运行 sed,只需指定通配符

    sed "s/old_text/new_text/g" *.xml -i
    

    要创建备份,只需在-i 后面指定扩展名:

    sed "s/old_text/new_text/g" *.xml -i~
    

    请注意,通常最好使用 XML 感知工具来处理 XML。

    【讨论】:

    • ...祈祷old_textnew_text 都不包含$, /, \1, &, ?, *, (, ), [, ], \+, ., etc。请注意,sed 不对字符串进行操作,它对具有受限字符集的正则表达式进行操作。请参阅*.com/questions/29613304/… 并考虑使用对字符串进行操作的工具,例如哦。您可以通过在命令周围使用单引号而不是双引号来稍微降低损坏的风险。
    • @EdMorton:是的,但是 awk 也不解析 XML :)
    • 如果你使用 XML 库就可以了,请参阅 gnu.org/software/gawk/manual/html_node/gawkextlib.html,但主要是我只是想提醒这个自称新手,他不会在 sed 中使用字符串。
    • @EdMorton:有趣,我不知道它们存在。可以提供链接吗?
    • 刚刚更新了我之前的评论以包含一个链接。另见sourceforge.net/projects/gawkextlib
    【解决方案2】:

    对于位于当前目录中的所有.xml 文件:

    sed -i.bak 's/old_text/new_text/g' *.xml
    

    要递归到子目录,请结合find:

    find . -name '*.xml' -exec sed -i.bak 's/old_text/new_text/g' '{}' \;
    

    这样备份文件将以.xml.bak结尾(-i的参数附加到原始文件名)。

    【讨论】:

    • 应该指出,这会创建所有 .xml 文件的 .bak 版本,而不仅仅是更新的文件。
    【解决方案3】:

    一个实用的 shell 脚本,如果您打算使用多种措施来清理一堆文件——这在一行上会变得有点不切实际......

    # only take files form certain subfolders and certain extensions
    
    # be careful to not tamper with .git or .svn folders 
    # - thus excluding all hidden folders as an extra precaution
    # - also tampering with node_modules is a bad idea
    
    FILES=$(find . -type f -regextype posix-extended     \
        -regex "^\./(public|source)/.*\.(scss|js)$"         \
        -not -regex ".*\/(\.|node_modules).*")
    
    for f in $FILES
    do
    echo "Processing $f file..."
    
    # all files: prune trailing whitespace on each file.
    sed -i 's/ *$//' $f
    
    if [[ $f =~ \.js$ ]]; then
        echo "javascript file!"
        # DO stuff
    fi
    
    if [[ $f =~ \.scss$ ]]; then
        echo "scss file!"
        # \b whole word matching – *.com/a/1032039/444255
        sed -i 's/\#000\b/black/g' $f
        sed -i 's/\#000000\b/black/g' $f
        sed -i 's/\#fff\b/white/g' $f
        sed -i 's/\#ffffff\b/white/g' $f
    fi
    
    done
    

    警告:权力越大,责任越大,大规模替代意味着权力越大……

    【讨论】:

    • 如果文件名包含空格,这将不起作用。