Рейтинг:6

Как проверить определенную строку с разрывами строк в файле с помощью grep?

флаг cn

У меня есть строковая переменная в файле сценария bash следующим образом:

строка = "

тест1

тест2

"

и я хочу проверить, является ли файл test.txt содержит эту конкретную строку (включая разрывы строк, т.е. она должна завершиться ошибкой, если она содержит только следующее:

Это тест:
тест1

тест2
и еще один

потому что разрывы строк выше test1 и ниже test2 отсутствуют.

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


Не работает следующее:

строка = "
    
    тест1
    
    тест2
    
    "
если ! grep -q строка "test.txt"; тогда
    эхо "$string" >> test.txt
фи

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


РЕДАКТИРОВАТЬ:

Ответы @terdon и @steeldriver ниже работают для примера строки, который я написал выше, но по какой-то причине они не работают для этого более реалистичного примера:

строка = "                                                                
                                                               
если [-f ~/.script]; тогда                            
        . ~/.скрипт         
фи

"  
user56834 avatar
флаг cn
@ Терранс, извини, не обращай внимания на мой предыдущий комментарий. На самом деле он по-прежнему не работает, но ошибка противоположна: теперь он никогда не настраивает файл, даже если строки там изначально нет. (Поэтому, если я выполню его 5 раз, а не 5 копий, как в случае с исходным кодом, я получу 0, тогда как должен получить 1).
terdon avatar
флаг cn
Ну да. Это совершенно другая ситуация, вы используете всевозможные специальные символы. Пожалуйста, [отредактируйте] свой вопрос и добавьте i) что именно вы делаете, какой подход вы используете; ii) как вы вызываете свой скрипт и iii) какую ошибку вы получаете (сообщение нам, что это ломается, не помогает нам понять).
user56834 avatar
флаг cn
@terdon, извините, да, мое сообщение было не очень ясным. i) я использовал как ваш подход, так и подход @steeldiver. Например.из вашего подхода я изменил только определение `string` ii) я вызываю его с помощью «bash substtest.sh» и iii) он не дает ошибки, а добавляет текст строки на неопределенный срок, если я вызываю bash substtest .sh снова и снова, а не просто добавляя его один раз.
terdon avatar
флаг cn
Какую команду вы выполняете, которая терпит неудачу? Как вы адаптировали мой ответ, чтобы он соответствовал вашим фактическим данным? Это совершенно другая ситуация по сравнению с вашим первоначальным вопросом. «Строка», которую вы ищете, содержит специальные символы. Вам понадобится что-то вроде `string='\n\nif \[ -f ~/.script \]; затем \s*\n\s*\. ~/\.script\s*\nfi\n\n'`.
terdon avatar
флаг cn
Смотрите обновленный ответ.
Рейтинг:6
флаг cn

Проблема в том, что grep будет выполняться в каждой строке, а не во всем файле. Пока файл достаточно мал, чтобы поместиться в память (что должно иметь место в подавляющем большинстве ситуаций в наши дни), вы можете использовать команду grep -z флаг, чтобы проглотить весь файл:

-z, --null-данные Рассматривайте входные и выходные данные как последовательности строк, каждая из которых заканчивается нулевым байтом (код ASCII NUL символ) вместо новой строки. Подобно опции -Z или --null, эту опцию можно использовать с командами например, sort -z для обработки произвольных имен файлов.

Следующая проблема заключается в том, что если вы пройдете grep что-то с новыми строками, он будет рассматривать это как список шаблонов для grep:

$ строка = "1
> 2"

$ последовательность 10 | grep "$ строка"
1
2
10
"

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

\n\nтест1\n\nтест2\n\n

Однако это также означает, что вам нужно флаг, чтобы включить Perl-совместимые регулярные выражения, чтобы \n буду работать.

Я создал эти два файла для демонстрации:

$ cat файл1
Это тест:
тест1

тест2
и еще один

$ cat файл2
Это тест:

тест1

тест2

и еще один

Используя эти два файла и приведенную выше информацию, вы можете сделать:

$ grep -Pz '\n\ntest1\n\ntest2\n\n' файл1
$ 

$ grep -Pz '\n\ntest1\n\ntest2\n\n' файл2
Это тест:

тест1

тест2

и еще один

Совокупность всего этого дает нам:

строка = '\n\ntest1\n\ntest2\n\n'
если ! grep -Pzq "$string" test.txt; тогда
    printf "$string" >> test.txt
фи

Или, как предложил @steeldriver в комментарии, вы можете использовать переменную и преобразовать новые строки в \n на лету:

$ строка = "

    тест1

    тест2

    "
$ если ! grep -Pzq "${string//$'\n'/\n}" test.txt; тогда
    printf "$string" >> test.txt
фи

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

searchString='\n\nif \[ -f ~/.script \]; затем\s*\n\s*\.\s+~/\.script\s*\nfi\n\n'
строка печати = '
если [-f ~/.script]; тогда
   . ~/.скрипт         
фи

'
если ! grep -Pzq "$searchString" test.txt; тогда     
    printf "%s" "$printString" >> test.txt 
фи
user56834 avatar
флаг cn
Спасибо! Я предполагаю, что вы имеете в виду «если! grep -q -z "$string" "test.txt"; then`, то есть с добавлением -z?
user56834 avatar
флаг cn
На самом деле, даже добавляя -z, для меня сохраняется та же проблема, о которой я говорил в комментарии к моему первоначальному вопросу: то есть либо с `if ! grep -q -z "$string" "test.txt"; тогда` или `если! grep -q "$string" "test.txt"; тогда` или `если! grep -q -z "$string" test.txt; then`, он терпит неудачу довольно странным образом:
terdon avatar
флаг cn
@ user56834 упс, да. Но на самом деле это не будет работать с переменной. Дайте мне несколько минут, я пытаюсь разобраться в проблеме.
Terrance avatar
флаг id
Кулио! +1 Наличие пробелов в строке как `string='\n\n test1\n\n test2\n\n'` работает так же хорошо. :)
terdon avatar
флаг cn
@ user56834 смотрите обновленный ответ.
terdon avatar
флаг cn
@steeldriver ага! Спасибо, я мог бы поклясться, что знал. Но нет, я просто проверил это в терминале и забыл. Исправлено сейчас, спасибо.
user56834 avatar
флаг cn
Извините за задержку, и спасибо! Что касается предложения steeldriver, я странно получаю сообщение об ошибке: «substtest.sh 12: неверная замена».
user56834 avatar
флаг cn
О, неважно, кажется, что это решается выполнением сценария .sh с помощью bash вместо sh (тире). Не уверен, почему. Оно работает! отличный. (Хотя я не понимаю часть "//$'\n'/\n}". Есть ли этому хорошее объяснение?)
terdon avatar
флаг cn
@user56834 user56834 `dash` и `sh` не являются `bash` и не должны рассматриваться как синонимы. Dash — это минимальная оболочка POSIX, в которой отсутствуют многие функции более сложной оболочки bash. То же самое касается «ш». Что касается `"${string//$'\n'/\n}"`, это (специфическая для bash) замена. Общий формат: `${var//old/new}`, который заменяет все вхождения `old` на `new` в переменной `$var`.Здесь «старый» — это `$'\n'`, который является способом передачи новой строки в оболочку.
user56834 avatar
флаг cn
На самом деле, я только что попробовал то же самое на более сложном случае, и это сломало его. Смотрите мой оригинальный вопрос.
Рейтинг:4
флаг hr

Возможно, вы захотите рассмотреть возможность использования pcregrep с или же --многострочный опция, позволяющая сопоставлять буквальные символы новой строки:

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

Бывший. данный

$ кошка test.txt
Это тест:
тест1

тест2
и еще один


    тест1

    тест2
    
    

и

$ кошка test2.txt
Это тест:
тест1

тест2
и еще один


    тест3

    тест4
    
    

с

$ строка = "

    тест1

    тест2

    "

тогда

$ pcregrep -qM "$string" test.txt && echo 'найдено' || эхо 'не найдено'
найденный

$ pcregrep -qM "$string" test2.txt && echo 'найдено' || эхо 'не найдено'
не найден
user56834 avatar
флаг cn
Спасибо, это работает. К сожалению, это не подходит для более реалистичного примера, который я добавил в свой вопрос (в этом примере просто ответ стердона не работает)
флаг hr
@ user56834 это, вероятно, потому, что `[ ... ]` обозначает диапазон символов в PCRE. Попробуйте заменить `"$string"` на `"\Q${string}\E"`
user56834 avatar
флаг cn
ответ немного позже, но: Не могли бы вы указать мне место, где я могу прочитать о том, что делают \Q и \E?
флаг hr
@ user56834 попробуйте perldoc [quotemeta] (https://perldoc.perl.org/functions/quotemeta)
Рейтинг:2
флаг cn

Поиск многострочных шаблонов в файле может быть проще с помощью awk:

awk '/Начальный шаблон/,/Конечный шаблон/' имя файла

Проверять эта почта для получения дополнительной информации

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.