диапазоны sed не всегда могут соответствовать только одной строке

dmidecode должен высказать Вам мнение в Ваш BIOS от ОС, обычно включает вентилятор и охлаждающиеся данные.

6
05.08.2014, 16:58
4 ответа

Да, это раздражает в sed (см. sed FAQ об этом). Поскольку вы используете GNU sed ( -r специфичен для GNU), вы можете сделать:

 sed -En "0,/$1/p"

(я предпочитаю -E , а не - r , поскольку он также поддерживается некоторыми другими sed , такими как FreeBSD, и совместим с grep и некоторыми другими инструментами (и будет в следующем выпуске стандартов POSIX / Single UNIX Specification )).

Лучшая альтернатива (и переносимая):

sed "/$1/q"

Сказать sed выйти (и прекратить чтение) после первого совпадения.

Обратите внимание, что awk не имеет проблемы, поэтому вы можете написать:

PATTERN=$1 awk 'NR==1, $0 ~ ENVIRON["PATTERN"]'

(хотя, как и для sed , вы бы предпочли написать):

PATTERN=$1 awk '1; $0 ~ ENVIRON["PATTERN"] {exit}'
6
27.01.2020, 20:23

Это нормальное поведение sed . Из документации POSIX sed :

Адреса в sed

Адрес - это либо десятичное число, которое считает входные строки кумулятивно по файлам символ '$', который указывает на последний строка ввода или адрес контекста (который состоит из BRE, как описан в регулярных выражениях в sed, с предшествующим и последующим разделитель, обычно косая черта).

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

Команда редактирования с одним адресом должна выбрать каждое пространство шаблона. который совпадает с адресом.

Команда редактирования с двумя адресами должна выбрать включающий диапазон. из первого пространства шаблонов, которое соответствует первому адресу, через следующее пространство шаблонов, соответствующее второму . (Если второй адрес число меньше или равно первому выбранному номеру строки, только должна быть выбрана одна строка.) Начиная с первой строки, следующей за выбранный диапазон, sed должен снова искать первый адрес. После этого процесс повторяется. Пропуск одного или обоих из компоненты адреса в следующей форме дают undefined результаты:

[адрес [, адрес]]

Как видите, sed напечатает включающий диапазон от первого адреса до следующего совпадающего адреса.

В вашем случае 1, / 1 / p , sed выводят первую строку, потому что она соответствует адресу 1 . Затем со второй строки sed будет искать второй адрес, соответствующий шаблону / 1 / . И остановите печать, если найдете. Поскольку из второй строки у вас нет шаблона, который соответствует / 1 / , поэтому sed напечатает остальное.

В случае с 1./2/p , sed напечатает первую строку, как указано выше, затем шаблон сопоставления второй строки / 2 / , sed print это и повторите действие для остальных. Но вы не можете сопоставить адрес 1 для остальных, поэтому sed ничего не печатает.

Пример:

$ echo 1 2 3 1 4 1 | tr ' ' $'\n' | sed -rn '1,/1/p'
1
2
3
1

Поскольку вы используете GNU sed , вы можете использовать форму 0, addr2 :

0,addr2
              Start  out  in  "matched  first  address"  state, until addr2 is
              found.  This is similar to 1,addr2, except that if addr2 matches
              the very first line of input the 0,addr2 form will be at the end
              of its range, whereas the 1,addr2 form  will  still  be  at  the
              beginning of its range.  This works only when addr2 is a regular
              expression.

Итак, ваша команда становится:

seq 1 4 | tr ' ' $'\n' | sed -rn '0,/'"$1"'/p'

Затем:

$ ./1.sh 1
1
4
27.01.2020, 20:23

Вы можете сделать несколько вещей. Например, ваш комментарий указывает, что вы хотите:

... удалить все от начала файла до определенной строки, и эта строка оказывается первой ...

Вы можете сделать это следующим образом:

sed -n "/$1"'/,$p'

Просто переверните форму. Приведенная выше команда будет печатать только из определенной строки до конца файла.

Если вы не хотите печатать эту конкретную строку ...

sed -n "/$1"'/,$p' | sed 1d

... должно сработать ...

Иначе вы могли бы просто обратиться к строке и взять цикл в свои руки.

seq 20 | sed -ne"/$1"'/!d;:B' -e'n;p;bB'
seq 20 | sed -n "/$1"'/!d;h;n;G;P;D'

Обе команды d удаляют каждую входящую строку, пока не встретят шаблон $ 1 .

Первый затем перезаписывает пространство шаблонов строкой ввода n ext и устанавливает метку : b . Затем он p очищает строку и снова перезаписывает пространство шаблонов внешней строкой n до того, как b вернется к : b метка. Таким образом он повторяется до конца файла. Эта команда, вероятно, быстрее второй - меньше.

Второй заменяет h старое пространство совпадением $ 1 . Затем он также перезаписывает пространство шаблонов строкой n ext на входе. Затем он G вставляет удерживаемое пространство и добавляет его к строке ввода, которую он только что втянул, тем самым меняя порядок двух строк и разделяя их символом новой строки. Примерно так:

  • строка 1> удерживать пробел

  • строка 2> строка 1

  • удерживать пробел >> строка 2

  • = строка 2 \ n строка 1

На данный момент sed P печатает только до первого \ n символа ewline, встречающегося в пространстве шаблонов, а D удаляет то же самое перед перезапуском цикла с остатком, который завершается всегда - это строка, которая первой соответствует $ 1 для в каждой строке . Таким образом, первая строка соответствует $ 1 всегда в пространстве шаблонов, но никогда не печатается .

Итак, если $ 1 равно 5 , печатается следующее:

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
3
27.01.2020, 20:23

Вы можете сохранить диапазон SED и делать без параметра -E или -R , при использовании функционального списка. После диапазона диапазон, содержащий команду Quit q (тестирован с FreeBSD & GNU SED ).

printf '%s\n' {1..10} | sed -n '1,/1/{p;q;}'

# your solution adapted to work with FreeBSD sed as well
re='/1/'
printf '%s\n' {1..4} | sed -En "1{$re{p;q;};}; 1,${re}p"
printf '%s\n' {1..4} | sed -En "1{/1/{p;q;};}; 1,//p"
0
27.01.2020, 20:23

Теги

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