Существует ли приложение командной строки, которое может найти определенный блок строк в текстовом файле и заменить его?

ОБНОВЛЕНИЕ (см. Конец вопроса)

Текст служебных программ «Поиск и замена», которые я видел, похоже, ищет только построчно .. .

Существует ли инструмент командной строки , который может найти один блок строк (в текстовом файле), и заменить его на еще один блок строк.

Например: содержит ли файл тестового файла эти 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!'

Я хочу это, чтобы я мог заменить несколько строк текста в файле и знаю, что я не перезаписываю неправильные строки.

Я бы никогда не заменил «Jabberwocky» (Льюис Кэрролл), но это новый пример:)

ОБНОВЛЕНИЕ :
.. ( суб-обновление) Мой следующий комментарий о причинах , когда не использует sed, только в контексте; не выдвигайте какой-либо инструмент слишком далеко за пределы его замысла (я довольно часто использую sed, и считаю его неоценимым.)

Я только что нашел интересную веб-страницу о sed и когда его не использовать.
Итак, из-за всех ответов sed я опубликую ссылку ... это часть sed FAQ по sourceforge

Также я довольно уверен, что есть какой-то способ diff сделать работу по поиску блока текста (как только он найден, замена довольно прямолинейна; используются head и tail) ... 'diff 'сбрасывает все необходимые данные, но я еще не понял, как их фильтровать, ... (я все еще работаю над этим)

7
задан 9 January 2011 в 23:30

5 ответов

Этот простой сценарий 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))

Как большинство других решений, это имеет недостаток, что целый файл хлебают в память сразу. Для небольших текстовых файлов это должно работать достаточно хорошо, как бы то ни было.

7
ответ дан 9 January 2011 в 23:30

Подход 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», выведите текст замены. Если текущая строка не попала в буфер, напечатайте содержимое буфера, напечатайте текущую строку и очистите буфер.

psusi показывает реализацию sed. В sed концепция буфера встроена; это называется трюмом. В awk или perl вы просто используете переменную (возможно, две, одну для содержимого буфера и одну для числа строк).

0
ответ дан 9 January 2011 в 23:30

Я был уверен, что должен быть способ сделать это с помощью sed. После некоторого поиска в Google я столкнулся с этим: 114] Исходя из этого, я закончил тем, что написал:

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

, который правильно взял содержимое x:

foo bar

и выплюнул :

Голова банки

0
ответ дан 9 January 2011 в 23:30

ОБНОВЛЕНИЕ: сценарий Python loevborg является, конечно, простым и лучшим решением (нет сомнения, что о том), и я очень доволен им, но я хотел бы указать, что сценарий удара, который я представил (в конце вопроса) нигде не рядом так сложен, как это смотрит.. Я обрезал все отбросы отладки, которые я раньше тестировал его.. и здесь это снова без перегружения (для любого посещающего эту страницу).. Это в основном a 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' решение, которое не столкнется ни с какими проблемами со специальными regex символами, потому что оно не использует даже один!.. вместо этого это работает над версиями Hexdumped файлов...

Я думаю, что это - также "вершина, тяжелая", но это работает и по-видимому не ограничивается никакими ограничениями размера.. GNU sed имеет неограниченный размер буфера шаблона, и это - то, где блок Hexdumped поисковых строк заканчивается.. Таким образом, это хорошо в этом отношении...

Я все еще ищу a diff решение, потому что это будет более гибко относительно пробела (и я ожидал бы; быстрее)..., но до тех пор.. Это - известный г-н Sed.:)

Этот сценарий полностью под управлением, как и обоснованно прокомментирован...
Это выглядит больше, который это; у меня есть только 7 строк существенного кода.
Для полуреалистического теста это загружает книгу "Алиса в Зазеркалье" с Проекта Gutenberg (363,1 КБ)... и заменяет исходное стихотворение Jabberwocky инвертированной строкой версией себя.. (Интересно, это не много другого чтения его назад :)

PS. Я просто понял, что слабость в этом методе - то, если Ваше исходное использование \r\n (0xODOA), как это - новая строка и Ваш "текст для соответствия", сохраняется с \n (0x0A).. затем этот процесс соответствия мертв в воде... ('разность' не имеет таких проблем)...


# 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'"
2
ответ дан 9 January 2011 в 23:30

Даже если Вам не нравится древний sed и perl, Вы могли бы все еще найти симпатию серого-templed цвета awk. Этот ответ, кажется, то, что Вы ищете. Я воспроизвожу его здесь. Скажите, что Вы имеете три файла и хотите заменить needle с replacement в haystack:


awk ' BEGIN { RS="" }
      FILENAME==ARGV[1] { s=$0 }
      FILENAME==ARGV[2] { r=$0 }
      FILENAME==ARGV[3] { sub(s,r) ; print }
    ' needle replacement haystack > output

Это не включает регулярные выражения и поддерживает символы новой строки. Это, кажется, работает с довольно большими файлами. Это действительно включает прихлебывание целого файла в память, таким образом, это не будет работать с файлами произвольного размера. Если Вы хотите это более изящный, можно включить целую хижину в сценарий удара или превратить ее в a awk сценарий.

2
ответ дан 9 January 2011 в 23:30

Другие вопросы по тегам:

Похожие вопросы: