【问题标题】:Find and Replace Inside a Text File from a Bash Command通过 Bash 命令在文本文件中查找和替换
【发布时间】:2010-10-06 06:02:07
【问题描述】:

在文件/tmp/file.txt 中查找和替换给定输入字符串(例如abc)并替换为另一个字符串(例如XYZ)的最简单方法是什么?

我正在编写一个应用程序并使用 IronPython 通过 SSH 执行命令 — 但我不太了解 Unix,也不知道要查找什么。

我听说 Bash 除了作为命令行界面之外,还可以是一种非常强大的脚本语言。所以,如果这是真的,我假设你可以执行这样的操作。

我可以用 bash 来做吗,实现我的目标的最简单(一行)脚本是什么?

【问题讨论】:

    标签: bash replace scripting ironpython


    【解决方案1】:

    最简单的方法是使用 sed(或 perl):

    sed -i -e 's/abc/XYZ/g' /tmp/file.txt
    

    由于-i 选项,它将调用 sed 进行就地编辑。这可以从 bash 中调用。

    如果你真的想只使用 bash,那么以下方法可以工作:

    while IFS='' read -r a; do
        echo "${a//abc/XYZ}"
    done < /tmp/file.txt > /tmp/file.txt.t
    mv /tmp/file.txt{.t,}
    

    这会遍历每一行,进行替换,然后写入临时文件(不想破坏输入)。最后的移动只是临时移动到原始名称。 (为了稳健性和安全性,临时文件名不应该是静态的或可预测的,但我们不要去那里。)

    对于 Mac 用户:

    sed -i '' 's/abc/XYZ/g' /tmp/file.txt(原因见下方评论)

    【讨论】:

    • 除了调用 mv 和使用 sed 一样“非 Bash”。我几乎对 echo 说了同样的话,但它是一个内置的 shell。
    • Solaris 不存在 sed 的 -i 参数(我认为还有其他一些实现),所以请记住这一点。只是花了几分钟弄清楚...
    • 自我注意:关于sed的正则表达式:s/..../..../ - Substitute/g - Global
    • 收到invalid command code C 错误的 Mac 用户的注意事项... 对于就地替换,BSD sed 需要在 -i 标志之后有一个文件扩展名,因为它保存一个带有给定的扩展名。例如:sed -i '.bak' 's/find/replace/' /file.txt 您可以使用空字符串跳过备份,如下所示:sed -i '' 's/find/replace/' /file.txt
    • 提示:如果您想不区分大小写,请使用s/abc/XYZ/gi
    【解决方案2】:

    文件操作通常不是由 Bash 完成,而是由 Bash 调用的程序完成,例如:

    perl -pi -e 's/abc/XYZ/g' /tmp/file.txt
    

    -i 标志告诉它进行就地替换。

    请参阅man perlrun 了解更多详情,包括如何备份原始文件。

    【讨论】:

    • 我的纯粹主义者说你不能确定 Perl 是否会在系统上可用。但如今这种情况很少见。也许我暴露了我的年龄。
    • 你能举一个更复杂的例子吗?比如用“chdir /blah2”替换“chdir /blah”。我试过perl -pi -e 's/chdir (?:\\/[\\w\\.\\-]+)+/chdir blah/g' text,但我一直收到错误,在-e第1行不推荐在模式和后面的单词之间没有空格。不匹配(在正则表达式中;由
    • @CMCDragonkai 检查这个答案:stackoverflow.com/a/12061491/2730528
    【解决方案3】:

    当我偶然发现这个时,我很惊讶......

    "mysql-server" 软件包附带了一个 replace 命令,所以如果您已经安装了它,请尝试一下:

    # replace string abc to XYZ in files
    replace "abc" "XYZ" -- file.txt file2.txt file3.txt
    
    # or pipe an echo to replace
    echo "abcdef" |replace "abc" "XYZ"
    

    有关更多信息,请参阅man replace

    【讨论】:

    • 这里有两件事可能:a) replace 是一个有用的独立工具,MySQL 人员应该单独发布它并依赖它 b) replace 需要一些 MySQL o_O 无论哪种方式,安装 mysql-server 以获取替换将是错误的做法:)
    • 只适用于mac?在我的ubuntu我centos该命令不存在
    • 那是因为你没有安装mysql-server 包。正如@rayro 所指出的,replace 是其中的一部分。
    • “警告:replace 已弃用,将在未来的版本中删除。”
    • 注意不要在 Windows 上运行 REPLACE 命令!在 Windows 上,REPLACE 命令用于快速复制文件。与本次讨论无关。
    【解决方案4】:

    这是一篇旧帖子,但对于任何想要使用变量的人来说,@centurian 说单引号意味着什么都不会被扩展。

    获取变量的一种简单方法是进行字符串连接,因为这是通过 bash 中的并置完成的,以下应该可以:

    sed -i -e "s/$var1/$var2/g" /tmp/file.txt
    

    【讨论】:

    • 我在文件中使用这个:sed "s/\"$li\"/- [x]\"\${li:5}\"/" $dat ang get sed unterminated `s' command
    • 问题解决了,好吧,... $li 来自文件行,所以有例如\n 并且错误在那里。所以要么 awk 要么像 python 这样的其他语言来了。
    【解决方案5】:

    Bash 和其他 shell 一样,只是一个协调其他命令的工具。通常你会尝试使用标准的 UNIX 命令,但你当然可以使用 Bash 来调用任何东西,包括你自己编译的程序、其他 shell 脚本、Python 和 Perl 脚本等。

    在这种情况下,有几种方法可以做到这一点。

    如果您想读取一个文件,并将其写入另一个文件,并在执行过程中进行搜索/替换,请使用 sed:

        sed 's/abc/XYZ/g' <infile >outfile
    

    如果您想就地编辑文件(就像在编辑器中打开文件,编辑它,然后保存它),请向行编辑器“ex”提供说明

        echo "%s/abc/XYZ/g
        w
        q
        " | ex file
    

    例如vi 没有全屏模式。您可以在vi: 提示符下给它同样的命令。

    【讨论】:

    • @awattar 在for 循环中一次完成一个。
    • 是否可以选择将它与 globbing 一起用作一个内衬?
    • 据我所知没有。 for f in report*.txt; do echo "%s/abc/XYZ/g \n w \n q \n" | ex file; done 简洁明了。为什么要将 shell 已有的功能放入 ex 中?
    • (或者,如果你的问题超出了 shell,使用 Python/Perl/whatever)
    【解决方案6】:

    我在其他帖子中找到了这个帖子,我同意它包含最完整的答案,所以我也添加了我的:

    1. seded 非常有用……手动操作。 看看@Johnny 的这段代码:

      sed -i -e 's/abc/XYZ/g' /tmp/file.txt
      
    2. 当我的限制是在 shell 脚本中使用它时,不能在里面使用任何变量来代替“abc”或“XYZ”。 BashFAQ 似乎至少同意我的理解。所以,我不能使用:

      x='abc'
      y='XYZ'
      sed -i -e 's/$x/$y/g' /tmp/file.txt
      #or,
      sed -i -e "s/$x/$y/g" /tmp/file.txt
      

      但是,我们能做些什么呢?正如@Johnny 所说,使用while read... 但不幸的是,这并不是故事的结局。以下对我很有效:

      #edit user's virtual domain
      result=
      #if nullglob is set then, unset it temporarily
      is_nullglob=$( shopt -s | egrep -i '*nullglob' )
      if [[ is_nullglob ]]; then
         shopt -u nullglob
      fi
      while IFS= read -r line; do
         line="${line//'<servername>'/$server}"
         line="${line//'<serveralias>'/$alias}"
         line="${line//'<user>'/$user}"
         line="${line//'<group>'/$group}"
         result="$result""$line"'\n'
      done < $tmp
      echo -e $result > $tmp
      #if nullglob was set then, re-enable it
      if [[ is_nullglob ]]; then
         shopt -s nullglob
      fi
      #move user's virtual domain to Apache 2 domain directory
      ......
      
    3. 正如人们所看到的那样,如果nullglob 被设置,当有一个包含* 的字符串时,它的行为很奇怪,如下所示:

      <VirtualHost *:80>
       ServerName www.example.com
      

      变成了

      <VirtualHost ServerName www.example.com
      

      没有结束尖括号,Apache2 甚至无法加载。

    4. 这种解析应该比一键搜索和替换要慢,但是,正如您已经看到的,在一个解析周期内有四个变量用于四种不同的搜索模式。

    对于给定的问题假设,我能想到的最合适的解决方案。

    【讨论】:

    • 在你的 (2) - 你可以做sed -e "s/$x/$y/",它会工作。不是双引号。如果变量中的字符串本身包含具有特殊含义的字符,则可能会造成严重混淆。例如,如果 x="/" 或 x="\"。当您遇到这些问题时,可能意味着您应该停止尝试使用 shell 来完成这项工作。
    【解决方案7】:

    你可以使用sed:

    sed -i 's/abc/XYZ/gi' /tmp/file.txt
    

    如果您不知道文件名,可以使用findsed

    find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;
    

    在所有 Python 文件中查找和替换:

    find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;
    

    【讨论】:

    • -i 不是“忽略大小写”,而是 -i[SUFFIX], --in-place[=SUFFIX](就地编辑文件(如果提供后缀,则进行备份))
    【解决方案8】:

    如果用“/”字符替换 URL,请小心。

    如何做的一个例子:

    sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"
    

    摘自:http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

    【讨论】:

      【解决方案9】:

      如果您正在处理的文件不是那么大,并且将其临时存储在变量中没有问题,那么您可以一次对整个文件使用 Bash 字符串替换 - 无需逐行查看:

      file_contents=$(</tmp/file.txt)
      echo "${file_contents//abc/XYZ}" > /tmp/file.txt
      

      整个文件内容将被视为一个长字符串,包括换行符。

      XYZ 可以是变量,例如$replacement,此处不使用 sed 的一个优点是您不必担心搜索或替换字符串可能包含 sed 模式分隔符(通常但不一定是 /) .缺点是不能使用正则表达式或任何 sed 更复杂的操作。

      【讨论】:

      • 在使用制表符时有什么技巧吗?出于某种原因,我的脚本在从带有大量转义的 sed 更改为此方法后找不到任何带有选项卡的内容。
      • 如果您想在要替换的字符串中放置一个制表符,您可以使用 Bash 的“美元单引号”语法来实现,因此制表符由 $'\t' 表示,并且您可以做 $ echo 'tab'$'\t''separated' > testfile; $ file_contents=$(
      【解决方案10】:

      您也可以使用ed 命令进行文件内搜索和替换:

      # delete all lines matching foobar 
      ed -s test.txt <<< $'g/foobar/d\nw' 
      

      在“Editing files via scripts with ed”中查看更多信息。

      【讨论】:

      • 此解决方案独立于 GNU/FreeBSD (Mac OSX) 的不兼容性(与 sed -i &lt;pattern&gt; &lt;filename&gt; 不同)。非常好!
      【解决方案11】:

      要以非交互方式编辑文件中的文本,您需要就地文本编辑器,例如 vim。

      以下是如何从命令行使用它的简单示例:

      vim -esnc '%s/foo/bar/g|:wq' file.txt
      

      这相当于ex编辑器的@slim answer,基本上是一回事。

      这里有几个ex 的实际例子。

      将文件中的文本foo 替换为bar

      ex -s +%s/foo/bar/ge -cwq file.txt
      

      删除多个文件的尾随空格:

      ex +'bufdo!%s/\s\+$//e' -cxa *.txt
      

      故障排除(终端卡住时):

      • 添加 -V1 参数以显示详细消息。
      • 强制退出:-cwq!

      另见:

      【讨论】:

      • 希望以交互方式进行替换。因此尝试了“vim -esnc '%s/foo/bar/gc|:wq' file.txt”。但是终端现在卡住了。我们如何在不让 bash shell 行为异常的情况下以交互方式进行替换。
      • 调试,加-V1,强制退出,使用wq!
      【解决方案12】:

      试试下面的shell命令:

      find ./  -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'
      

      【讨论】:

      • 这是一个很好的回答“我怎么不小心把所有子目录中的所有文件也弄错了”,但这似乎不是这里要问的。
      • 此语法不适用于sed 的BSD 版本,请改用sed -i''
      【解决方案13】:

      您也可以在 bash 脚本中使用 python。我在这里的一些最重要的答案并没有取得太大的成功,并且发现它不需要循环就可以工作:

      #!/bin/bash
      python
      filetosearch = '/home/ubuntu/ip_table.txt'
      texttoreplace = 'tcp443'
      texttoinsert = 'udp1194'
      
      s = open(filetosearch).read()
      s = s.replace(texttoreplace, texttoinsert)
      f = open(filetosearch, 'w')
      f.write(s)
      f.close()
      quit()
      

      【讨论】:

        【解决方案14】:

        使用 sed 命令替换文件中多个文本的最简单方法

        命令 -

        sed -i 's#a/b/c#D/E#g;s#/x/y/z#D:/X#g;'文件名

        在上面的命令 s#a/b/c#D/E#g 中,我将 a/b/c 替换为 D/E,然后在 ;我们再次做同样的事情

        【讨论】:

          【解决方案15】:

          您可以使用 rpl 命令。例如你想在整个 php 项目中更改域名。

          rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  
          

          这不是明确的原因,但它非常快速且有用。 :)

          【讨论】:

            【解决方案16】:

            如果您不阅读 cmets,MAC 用户 :)

            正如@Austin 所述,如果您收到Invalid command code 错误

            对于就地替换,BSD sed 需要在 -i 标志之后的文件扩展名以保存到具有给定扩展名的备份文件。

            sed -i '.bak' 's/find/replace' /file.txt

            如果你想跳过备份,你可以使用''空字符串。

            sed -i '' 's/find/replace' /file.txt

            @Austin 所有的功劳

            【讨论】:

              【解决方案17】:

              如果同时对多个文件进行更改,我们可以在一行中执行以下操作:-

              user_name='whoami'
              for file in file1.txt file2.txt file3.txt; do sed -i -e 's/default_user/${user_name}/g' $file; done
              

              如果可能有用,则添加。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-07-29
                • 1970-01-01
                • 1970-01-01
                • 2010-12-26
                相关资源
                最近更新 更多