команда sed внутри, пока цикл не работает для ubuntu

avatar
Eng-Mohammed Kayed
8 августа 2021 в 17:58
169
2
-1

У меня есть два файла; первый включает шаблоны (file.txt), которые я хочу найти во втором файле (file.cfg).

После того, как шаблон найден в "file.cfg", я хочу удалить его + все, что идет после него, до следующего приветствия, которое идет в начале строки.

Я создал приведенный ниже скрипт, но он не работает:

#! /bin/bash
cat file.txt | while read LINE; do
echo $LINE
    sed -i "/^$LINE$/,/^Hello/{//p;d;}" "file.cfg"
    sed -i "/^$LINE$/d" "file.cfg"
done

Вчера он работал нормально с тестовыми файлами. Сегодня я изменил имя файла, и он перестал работать.

Я не уверен. Если я что-то изменил по ошибке, но если я воспользуюсь приведенной ниже командной строкой Ubuntu, это сработает:

sed -i "/^Hello World$/,/^Hello/{//p;d;}" "file.cfg"

Кроме того, я добавил эхо в цикл, и я могу видеть каждую строку в "file.txt"

Чтобы предоставить дополнительную информацию, я приведу пример того, чего мне нужно достичь с помощью этого кода:

"file.txt" содержит шаблоны. Мне нужно найти совпадение в "file.cfg", как только шаблон будет найден, мне нужно удалить его, и все, что следует после него, до следующего Hello.

sed -i "/^$LINE$/,/^Hello/{//p;d;}" "file.cfg" -- > эта строка должна удалить все промежуточные значения.

sed -i "/^$LINE$/d" "file.cfg" --- > удалить сам шаблон.

++++++++++

См. пример ниже:

Файл.cfg разбит на секции; каждый раздел начинается с Hello

Файл.txt содержит случайные имена разделов; Мне нужен скрипт, чтобы прочитать имя раздела из File.txt и посмотреть, доступен ли он в file.cfg, а затем удалить имя раздела и все его содержимое

Файл.txt :

Hello World
Hello Mohammad
Hello Scripting

Файл.cfg :

Hellow xyz
a
b
c
Hello World
v
b
n
Hello stack
q
w
e

Окончательные результаты должны быть:

Hellow xyz
a
b
c
Hello stack
q
w
e

После того, как имя раздела найдено, мне нужно удалить все до следующего приветствия, которое появляется в начале строки (новый раздел).

Ни одна из строк не начинается с Hello, кроме имени раздела.

Источник
tripleee
8 августа 2021 в 18:33
0

Многократная перезапись одного и того же файла с sed -i в цикле неэффективна и подвержена ошибкам. Вероятно, попробуйте провести рефакторинг, чтобы создать один скрипт sed из входного файла. И попробуйте shellcheck.net, чтобы вам указали на некоторые другие распространенные ошибки в вашем коде.

Ed Morton
8 августа 2021 в 20:43
0

Пожалуйста, прочитайте почему-это-использование-цикла-оболочки-для-обработки-текста-считается-плохой-практикой, чтобы понять некоторые проблемы с вашим сценарием, это-возможно- to-escape-regex-metacharacters-reliably-with-sed для других и скопируйте/вставьте его в shellcheck.net для получения дополнительной информации. отредактируйте свой вопрос, включив в него краткий, проверяемый образец входных данных и ожидаемый результат, чтобы мы могли вам помочь.

Ed Morton
8 августа 2021 в 20:45
0

И не используйте слово «шаблон» при указании требований для сопоставления текста, поскольку это очень двусмысленно, вместо этого используйте регулярное выражение или строку плюс полное или частичное. См. раздел как найти текст, соответствующий шаблону.

Eng-Mohammed Kayed
8 августа 2021 в 20:48
0

Спасибо за документы. Не могли бы вы помочь мне исправить мой код? Я все еще новичок в написании сценариев, и я должен заставить его работать сегодня.

Ed Morton
8 августа 2021 в 20:52
0

Мы определенно можем помочь вам исправить ваш код, но нам нужно, чтобы вы сказали нам, что он должен делать, и сначала привели пример. В настоящее время мы даже не знаем, следует ли рассматривать $LINE как регулярное выражение или как строку (например, должно ли a.c в LINE соответствовать abc в файле .cfg или нет?). Пожалуйста, отредактируйте свой вопрос, чтобы, как минимум, заменить «шаблон» регулярным выражением или строкой и добавить образец ввода и ожидаемый вывод, который демонстрирует, что вам нужно от сценария, и мы можем скопировать / вставить для тестирования.

Ed Morton
8 августа 2021 в 20:54
0

@triplee сделал предположение о том, что вы можете захотеть, и мне это показалось разумным предположением, но, видимо, это не то, что вы хотите, и вы не сказали, каким образом это не удалось, поэтому нет смысла кому-либо еще строить предположения, когда вы мог бы просто рассказать и показать нам.

Eng-Mohammed Kayed
8 августа 2021 в 21:12
0

Заранее спасибо, я добавил больше деталей, пожалуйста, дайте мне знать, если теперь все ясно

Ed Morton
8 августа 2021 в 22:04
0

Кажется, теперь все ясно, поэтому я добавил решение.

Beta
9 августа 2021 в 00:41
0

Что вы имеете в виду, когда говорите, что это «не работает»? Это вообще меняет текст? Выдает сообщение об ошибке? Что произойдет, если удалить -i? Если вы поместите echo перед командами sed в скрипте, что вы увидите?

Ответы (2)

avatar
Cole Tierney
9 августа 2021 в 11:29
0

Мне нравится предложение @tripleee создать сценарий sed из файла шаблонов. Это приводит к тому, что один проход и sed делают sed апеллирующим к моему чувству юмора :)

Первым шагом является создание сценария sed:

sed 's|.*|/^&$/, /^Hello/ {\n\t/^&$/ d\n\t/^Hello/! d\n}|' file.txt
/^Hello World$/, /^Hello/ {
    /^Hello World$/ d
    /^Hello/! d
}
/^Hello Mohammad$/, /^Hello/ {
    /^Hello Mohammad$/ d
    /^Hello/! d
}
/^Hello Scripting$/, /^Hello/ {
    /^Hello Scripting$/ d
    /^Hello/! d
}

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

Я сгенерирую приведенный выше sed, используя подстановку процесса bash, и буду обращаться с ним как с программным файлом sed (или его можно поместить во временный файл):

#!/bin/bash

sed -f <(
    sed 's|.*|/^&$/, /^Hello/ {\n\t/^&$/ d\n\t/^Hello/! d\n}|' file.txt
) file.cfg

Я пропустил параметр редактирования -i на месте для тестирования.

Для неразрушающего контроля сравните ожидаемые результаты с выводом скрипта:

diff expect <(./remove.sh) && echo ok
avatar
Ed Morton
8 августа 2021 в 21:59
1
$ awk 'NR==FNR{names[$0]; next} $1=="Hello"{f=($0 in names)} !f' File.txt File.cfg
Hellow xyz
a
b
c
Hello stack
q
w
e

Если вы хотите выполнять редактирование "на месте", тогда, как и в GNU sed, который вы сейчас используете, есть -i, в GNU awk есть -i inplace, но учтите, что вы работаете с двумя входными файлами, поэтому вам нужно написать обоим:

awk -i inplace 'NR==FNR{names[$0]; print; next} $1=="Hello"{f=($0 in names)} !f' File.txt File.cfg

или активируйте редактирование на месте только для второго, см. справочную страницу gawk, чтобы узнать, как это контролировать. ИМХО проще использовать временный выходной файл:

tmp=$(mktemp) &&
awk 'NR==FNR{names[$0]; next} $1=="Hello"{f=($0 in names)} !f' File.txt File.cfg > "$tmp" &&
mv -- "$tmp" File.cfg
Eng-Mohammed Kayed
8 августа 2021 в 22:36
0

Спасибо за код. На самом деле мой файл большой, поэтому я не могу запустить команду и проверить результат без создания нового файла или перезаписи исходного, я попытался добавить -i на место, но он очистил содержимое File.txt, Любые идеи?

Ed Morton
8 августа 2021 в 23:25
0

Что касается my file is large so... - это верно для любой команды, запускаемой для файла любого размера, который вы не можете verify the result without creating a new file or overwriting the original one, так что не знаю, почему вы это сказали. Да, если вы запустите скрипт как есть, тогда -i inplace очистит File.txt, потому что скрипт ничего не печатает, пока читает File.txt в блоке NR==FNR. Измените next на print; next, если вы хотите использовать -i inplace.

Ed Morton
8 августа 2021 в 23:31
0

Я добавил в свой ответ дополнительную информацию о том, как обновить File.cfg.