是否有一个命令行实用程序应用程序可以找到文本文件中的一个特定行块并替换它?

是否有一个命令行实用程序应用程序可以找到文本文件中的一个特定行块并替换它?

更新(见问题末尾)

我见过的文本“搜索和替换”实用程序似乎只能逐行搜索......

有没有命令行可以的工具定位一行代码(在文本文件中)代替它与另一块线条有关。?

例如:测试文件是否包含此文件exact group行数:

'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
All mimsy were the borogoves,  
And the mome raths outgrabe. 

'Beware the Jabberwock, my son!
The jaws that bite, the claws that catch!
Beware the Jubjub bird, and shun
The frumious Bandersnatch!'

我想要这个,这样我就可以替换文件中的多行文本,并且知道我没有覆盖错误的行。

我永远不会取代“The Jabberwocky”(刘易斯·卡罗尔),但它是一个新颖的例子:)

更新
..(子更新)我对原因的以下评论什么时候不使用 sed仅有的在上下文中;不要将任何工具推得太超出其设计意图(我经常使用 sed,并认为它非常有价值。)

我刚刚发现了一个有趣的网页sed以及何时不使用它。
因此,由于sed答案,我会发布链接。它是sourceforge 上的 sed 常见问题解答

此外,我很确定有办法diff可以完成定位文本块的工作(一旦找到它,替换就非常简单了;使用headtail)...'diff'转储所有必要的数据,但我还没有想出如何过滤它,......(我仍在努力)

答案1

这个简单的 Python 脚本可以完成以下任务:


#!/usr/bin/env python

# Syntax: multiline-replace.py input.txt search.txt replacement.txt

import sys

inp = open(sys.argv[1]).read()
needle = open(sys.argv[2]).read()
replacement = open(sys.argv[3]).read()

sys.stdout.write(inp.replace(needle,replacement))

像大多数其他解决方案一样,它的缺点是整个文件会立即被放入内存中。但是,对于小型文本文件,它应该足够好用。

答案2

方法 1:暂时将换行符改为其他内容

以下代码片段将换行符与竖线交换,执行替换,并将分隔符交换回来。如果实用程序发现行过长,则可能会阻塞。您可以选择任何字符进行交换,只要它不在您的搜索字符串中即可。

<old.txt tr '\n' '|' |
sed 's/\(|\|^\)'\''Twas … toves|Did … Bandersnatch!'\''|/new line 1|new line 2|/g' |
tr '|' '\n' >new.txt

方法 2:更改实用程序的记录分隔符

Awk 和 perl 支持设置两个或更多的空白行作为记录分隔符。使用 awk,传递-vRS=(空RS变量)。使用 Perl,传递-000(“段落模式”)或设置$,=""。但这在这里没有帮助,因为您有一个多段落搜索字符串。

awk 和 perl 还支持将任何字符串设置为记录分隔符。将RS或设置$,为不在搜索字符串中的任何字符串。

<old.txt perl -pe '
    BEGIN {$, = "|"}
    s/^'\''Twas … toves\nDid … Bandersnatch!'\''$/new line 1\nnew line 2/mg
' >new.txt

方法 3:处理整个文件

一些实用程序可以让您轻松地将整个文件读入内存并对其进行处理。

<old.txt perl -0777 -pe '
    s/^'\''Twas … toves\nDid … Bandersnatch!'\''$/new line 1\nnew line 2/mg
' >new.txt

方法 4:编程

逐行读取行。从空缓冲区开始。如果您看到“'Twas”行并且缓冲区为空,请将其放入缓冲区。如果您看到“Did gyre”并且缓冲区中有一行,请将当前行附加到缓冲区,依此类推。如果您刚刚附加了“Bandersnatch 行”,请输出替换文本。如果当前行未进入缓冲区,请打印缓冲区内容,打印当前行并清空缓冲区。

普苏西显示了 sed 的实现。在 sed 中,缓冲区概念是内置的;它被称为保留空间。在 awk 或 perl 中,您只需使用一个变量(可能两个,一个用于缓冲区内容,一个用于行数)。

答案3

我确信一定有办法用 sed 来实现这一点。经过一番谷歌搜索,我找到了这个:

http://austinmatzko.com/2008/04/26/sed-multi-line-search-and-replace/

基于此我最终写道:

sed -n '1h;1!H;${;g;s/foo\nbar/jar\nhead/g;p;}' < x

它正确地获取了 x 的内容:

富吧

然后吐出:

罐头

答案4

更新:loevborg 的 python 脚本无疑是最简单、最好的解决方案(毫无疑问),我对它非常满意,但我想指出的是,我提供的 bash 脚本(在问题的末尾)远没有看起来那么复杂。我删掉了用来测试它的所有调试糟粕。这里再次出现了,没有负担(对于访问此页面的任何人来说)。它基本上是一行代码sed,带有前后十六进制转换:

F=("$haystack"  "$needle"  "$replacement")
for f in "${F[@]}" ; do cat "$f" | hexdump -v -e '1/1 "%02x"' > "$f.hex" ; done
sed -i "s/$(cat "${F[1])}.hex")/$(cat "${F[2])}.hex")/p" "${F[0])}.hex"
cat "${F[0])}.hex" | xxd -r -p > "${F[0])}"
# delete the temp *.hex files.

只是为了参加比赛,我想出了一个“sed”解决方案,它不会遇到任何问题特别的正则表达式字符,因为它甚至不使用一个!...相反,它适用于文件的十六进制转储版本......

我认为它太“头重脚轻”,但它可以工作,而且显然不受任何大小限制。GNU sed 有无限的图案缓冲区大小,这就是十六进制转储的搜索行块最终到达的地方。所以从这个方面来说这是没问题的……

我仍在寻找diff解决方案,因为它在空白方面将更加灵活(我希望更快)......但在此之前......这是著名的 Sed 先生。:)

该脚本完全按原样运行,并有合理的注释...
它看起来比实际要大;我只有7行基本代码。
为了进行半现实的测试,它从以下位置下载了《爱丽丝梦游仙境》这本书古腾堡计划(363.1 KB)... 并用一行反转的版本替换原始的 Jabberwocky 诗歌。(有趣的是,从后往前读也没什么区别:)

PS. 我刚刚意识到这种方法的一个弱点是,如果你的原始文本使用 \r\n (0xODOA) 作为换行符,而你的“要匹配的文本”使用 \n (0x0A) 保存...那么这个匹配过程就失败了... ('diff' 没有这样的问题)...


# In a text file, replace one block of lines with another block
#
# Keeping with the 'Jabberwocky' theme, 
#  and using 'sed' with 'hexdump', so 
#  there is no possible *special* char clash.
# 
# The current setup will replace only the first instance.
#   Using sed's 'g' command, it cah change all instances. 
#

  lookinglass="$HOME/Through the Looking-Glass by Lewis Carroll"
  jabberwocky="$lookinglass (jabberwocky)"
  ykcowrebbaj="$lookinglass (ykcowrebbaj)"

  ##### This section if FOR TEST PREPARATION ONLY
        fromURL="http://www.gutenberg.org/ebooks/12.txt.utf8"
        wget $fromURL -O "$lookinglass"
        if (($?==0))
        then  echo "Download OK"
        else  exit 1
        fi
        # Make a backup of the original (while testing)
        cp "$lookinglass" "$lookinglass(fromURL)"
        #
        # Extact the poem and write it to a file. (It runs from line 322-359)
        sed -n 322,359p "$lookinglass" > "$jabberwocky"
        cat "$jabberwocky"; read -p "This is the original.. (press Enter to continue)"
        #
        # Make a file containing a replacement block of lines
        tac "$jabberwocky" > "$ykcowrebbaj"
        cat "$ykcowrebbaj"; read -p "This is the REPLACEMENT.. (press Enter to continue)"
  ##### End TEST PREPARATION

# The main process
#
# Make 'hexdump' versions of the 3 files... source, expected, replacement 
  cat "$lookinglass" | hexdump -v -e '1/1 "%02x"' > "$lookinglass.xdig"
  cat "$jabberwocky" | hexdump -v -e '1/1 "%02x"' > "$jabberwocky.xdig"
  cat "$ykcowrebbaj" | hexdump -v -e '1/1 "%02x"' > "$ykcowrebbaj.xdig"
# Now use 'sed' in a safe (no special chrs) way.
# Note, all files are now each, a single line  ('\n' is now '0A')
  sed -i "s/$(cat "$jabberwocky.xdig")/$(cat "$ykcowrebbaj.xdig")/p" "$lookinglass.xdig"

  ##### This section if FOR CHECKING THE RESULTS ONLY
        # Check result 1
        read -p "About to test for the presence of  'jabberwocky.xdig'  within itself (Enter) "
        sed -n "/$(cat "$jabberwocky.xdig")/p"     "$jabberwocky.xdig"
        echo -e "\n\nA dump above this line, means: 'jabberwocky' is as expected\n" 
        # Check result 2
        read -p "About to test for the presence of  'ykcowrebbaj.xdig'  within itself (Enter) "
        sed -n "/$(cat "$ykcowrebbaj.xdig")/p"     "$ykcowrebbaj.xdig"
        echo -e "\n\nA dump above this line, means: 'ykcowrebbaj' is as expected\n" 
        # Check result 3
        read -p "About to test for the presence of  'lookinglass.xdig'  within itself (Enter) "
        sed -n "/$(cat "$ykcowrebbaj.xdig")/p"     "$lookinglass.xdig"
        echo -e "\n\nA dump above this line, means: 'lookinglass' is as expected\n" 
        # Check result 4
        read -p "About to test for the presence of  'lookinglass.xdig'  within itself (Enter) "
        sed -n "/$(cat "$jabberwocky.xdig")/p"     "$lookinglass.xdig"
        echo -e "\n\nNo dump above this line means: 'lookinglass' is as expected\n"
  ##### End of CHECKING THE RESULTS

# Now convert the hexdump to binary, and overwrite the original
  cat "$lookinglass.xdig" | xxd -r -p > "$lookinglass"
# Echo the "modified" poem to the screen
  sed -n 322,359p "$lookinglass"
  echo -e "\n\nYou are now looking at the REPLACEMENT text (dumped directly from the source 'book'"

相关内容