【问题标题】:Shell script - search and replace text in multiple files using a list of stringsShell 脚本 - 使用字符串列表搜索和替换多个文件中的文本
【发布时间】:2010-10-13 12:40:04
【问题描述】:

我有一个文件“changesDictionary.txt”,其中包含(数量可变的)键值字符串对。

例如

"textToSearchFor" = "theReplacementText"

(字典的格式不重要,可根据需要更改。)

我需要遍历给定目录的内容,包括子目录。对于遇到的每个扩展名为“.txt”的文件,我们在 changesDictionary.txt 中搜索 每个 键,用替换字符串值替换每个找到的实例。

即对多个文件进行搜索和替换,但使用搜索/替换术语列表而不是单个搜索/替换术语。

我怎么能这样做? (我研究过单个搜索/替换示例,但不明白如何在一个文件中进行多个搜索。)

实现(bash、perl 等)并不重要,只要我可以在 Mac OS X 的命令行中运行它即可。感谢您的帮助。

【问题讨论】:

    标签: unix replace


    【解决方案1】:

    我会将您的 changesDictionary.txt 文件转换为 sed 脚本,使用... sed:

    $ sed -e 's/^"\(.*\)" = "\(.*\)"$/s\/\1\/\2\/g/' \
          changesDictionary.txt  > changesDictionary.sed
    

    注意,字典中正则表达式或 sed 表达式的任何特殊字符都将被 sed 错误解释,因此您的字典要么只有最原始的搜索和替换,要么您需要使用有效的表达式维护 sed 文件。不幸的是,在 sed 中没有简单的方法来关闭正则表达式并仅使用字符串匹配或将您的搜索和替换引用为“文字”。

    使用生成的 sed 脚本,使用 find and xargs -- 而不是 find -exec -- 尽快使用 sed 脚本转换您的文件,方法是一次处理多个文件时间。

    $ find somedir -type f -print0 \
       | xargs -0 sed -i -f changesDictionary.sed
    

    注意,sed 的-i 选项“就地”编辑文件,因此请务必进行备份以确保安全,或使用-i~ 创建波浪号备份。

    最后说明,使用搜索和替换可能会产生意想不到的后果。您的搜索是否是其他搜索的子字符串?这是一个例子。

    $ cat changesDictionary.txt
    "fix" = "broken"
    "fixThat" = "Fixed"
    $ sed -e 's/^"\(.*\)" = "\(.*\)"$/s\/\1\/\2\/g/' changesDictionary.txt  \
       | tee changesDictionary.sed
    s/fix/broken/g
    s/fixThat/Fixed/g
    $ mkdir subdir
    $ echo fixThat > subdir/target.txt
    $ find subdir -type f -name '*.txt' -print0 \
       | xargs -0 sed -i -f changesDictionary.sed
    $ cat subdir/target.txt
    brokenThat
    

    “fixThat”应该变成“Fixed”还是“brokenThat”? sed 脚本的顺序很重要。类似地,搜索和替换可以多次搜索和替换——将“a”更改为“b”,稍后可能会被另一个搜索和替换从“b”更改为“c”。

    也许你已经考虑过这两个,但我提到是因为我已经尝试过你之前正在做的事情并且没有想到它。我不知道有什么可以简单地做正确的事情一次进行多个搜索和替换。因此,您需要对其进行编程以自己做正确的事情。

    【讨论】:

    • 感谢您的帮助!我还在学习 shell 脚本,所以会一步一步地完成并报告。
    【解决方案2】:

    这是我会做的基本步骤

    1. 复制 changesDictionary.txt 文件
    2. 在其中将 "a"="b" 替换为等效的 sed 行:例如(使用 $1 作为文件名)

      sed -e 's/a/b/g' $1

      (您可以编写一个脚本来执行此操作,或者只是手动执行,如果您只需要执行一次并且它不是太大)。

    3. 如果文件都在一个目录中,那么您可以执行以下操作:

      ls *.txt | xargs scriptFromStep2.sh

    4. 如果它们在子目录中,请使用 find 对所有文件调用该脚本,例如

      找到 . -name '*.txt' -exec scriptFromStep2.sh {} \;

    这些都不准确,做一些实验以确保你做对了——这只是我会使用的方法。

    (不过,如果可以的话,就用perl,会简单很多)

    【讨论】:

    • 谢谢你的帮助,娄。我应该指出 bash 根本不是必需的。目标平台是 Mac OS X,所以我可以从命令行运行的任何东西都可以完成这项工作。我是 bash 的新手,所以 perl(或其他任何东西)实际上更可取。
    • 哦,我要补充一点,要处理的文件肯定在嵌套的子目录中。
    • 还有一件事:字典文件的格式并不重要。我可以将其更改为需要的任何内容。
    • 运行 echo a | sed '%s/a/b/g' 给出“-e 表达式 #1,字符 1:未知命令:`%'”。错字还是 sed 的一些扩展?
    • 去掉 % (这是一个错误——来自 vim 中的 ed 使用方式)
    【解决方案3】:

    使用这个工具,它是用 Perl 编写的 - 有很多花里胡哨 - 老了,但很好:

    http://unixgods.org/~tilo/replace_string/

    特点:

    • 执行多个搜索替换或查询搜索替换操作
    • 搜索替换表达式可以在命令行中给出或从文件中读取
    • 处理多个输入文件
    • 递归地进入目录并对所有文件进行多次搜索/替换操作
    • 用户定义的 perl 表达式应用于每个输入文件的每一行
    • 可选择以段落模式运行(用于多行搜索/替换)
    • 交互模式
    • 批处理模式
    • 可选择备份文件和备份编号
    • 以 root 身份运行时保留模式/所有者
    • 忽略符号链接、空文件、写保护文件、套接字、命名管道和目录名
    • 可选地替换仅匹配/不匹配给定正则表达式的行

    多年来,该脚本已被广泛用于大型数据集。

    【讨论】:

      【解决方案4】:
      #!/bin/bash
      f="changesDictionary.tx"
      find /path -type f -name "*.txt" | while read FILE 
      do
          awk 'BEGIN{ FS="=" }
          FNR==NR{ s[$1]=$2;  next }
          {
             for(i in s){      
              if( $0 ~ i ){ gsub(i,s[i]) }
             }
             print $0
          }' $f $FILE  > temp
          mv temp $FILE
      done
      

      【讨论】:

        猜你喜欢
        • 2021-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-20
        • 2011-03-14
        • 2020-05-12
        • 2023-02-02
        • 2018-09-26
        相关资源
        最近更新 更多