Использование одного вызова sed для разделения и grep - PullRequest
4 голосов
/ 26 июня 2019

Это в основном из любопытства, я пытаюсь вести себя так же, как:

echo -e "test1:test2:test3"| sed 's/:/\n/g' | grep 1

в одной команде sed.

Я уже пробовал

echo -e "test1:test2:test3"| sed -e "s/:/\n/g" -n "/1/p"

Но я получаю следующую ошибку:

sed: can't read /1/p: No such file or directory

Есть идеи, как это исправить и объединить различные типы команд в один sed вызов?

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

РЕДАКТИРОВАТЬ: меня в основном интересует инструмент sed, я ужезнать, как это сделать, используя другие инструменты или даже их комбинации.
РЕДАКТИРОВАТЬ2: Вот более реалистичный сценарий, более близкий к тому, что я пытаюсь достичь:

arch=linux64
base=https://chromedriver.storage.googleapis.com
split="<Contents>"
curl $base \
    | sed -e 's/<Contents>/<Contents>\n/g' \
    | grep $arch \
    | sed -e 's/^<Key>\(.*\)\/chromedriver.*/\1/' \
    | sort -V > out

Что я хотел быупрощение - это линия скручивания, превращающая ее в нечто вроде:

curl $base \
 | sed 's/<Contents>/<Contents>\n/g' -n '/1/p' -e 's/^<Key>\(.*\)\/chromedriver.*/\1/' \
 | sort -V > out

Ответы [ 5 ]

3 голосов
/ 26 июня 2019

Вот несколько альтернатив, awk и sed на основе:

sed -E "s/(.*:)?([^:]*1[^:]*).*/\2/" <<< "test1:test2:test3"
awk -v RS=":" '/1/' <<< "test1:test2:test3"
# or also 
awk 'BEGIN{RS=":"} /1/' <<< "test1:test2:test3"

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

sed "s/:/\n/g" <<< "test1:test2:test3" | sed -n "/1/p"

См. это онлайн демо . Решение awk выглядит максимально чистым.

Детали

В решении sed шаблон (.*:)?([^:]*1[^:]*).* соответствует необязательной последовательности любых 0+ символов и :, затем захватывает в группу 2 любые 0 или более символов, отличных от :, 1, снова 0 или больше символов, отличных от :, а затем просто соответствует остальной части строки. При замене сохраняется только содержимое группы 2.

В решении awk разделитель записей установлен на :, а затем /1/ регулярное выражение используется только для возврата записи, содержащей 1.

2 голосов
/ 27 июня 2019

Это может сработать для вас (GNU sed):

sed 's/:/\n/;/^[^\n]*1/P;D' file

Замените каждый : и, если первая строка в пространстве шаблона содержит 1, напечатайте его. Повторите.

Альтернатива:

sed -Ez 's/:/\n/g;s/^[^1]*$//mg;s/\n+/\n/;s/^\n//' file

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

1 голос
/ 26 июня 2019

Альтернатива действительно уродливому седу: grep -o '\w*2\w*'

$ printf "test1:test2:test3\nbob3:bob2:fred2\n"  | grep -o '\w*2\w*'
test2
bob2
fred2
  • grep -o: только соответствие

Или: grep -o '[^:]*2[^:]*'

1 голос
/ 26 июня 2019

echo -e "test1:test2:test3" | sed -En 's/:/\n/g;/^[^\n]*2[^\n]*(\n|$)/P;//!D'

  • sed -n не печатается, если только
  • sed -E не позволяет использовать парены для соответствия (\n|$), что является новой строкой или концомшаблонное пространство
  • P печатает буфер шаблонов до первой новой строки.
  • D обрезает буфер шаблонов до первой новой строки
  • [^\n] являетсяКласс символов, который соответствует чему-либо, кроме новой строки
  • //, является сокращенным отредактированным для повторения совпадения
  • //! затем сопоставляет все, что не соответствовало ранее

Итак, после разделения на новые строки вы хотите убедиться, что символ 2 находится между началом буфера шаблонов ^ и первой новой строкой.

И, если символа нетвы ищете, вы хотите D удалить до первой новой строки.

В этот момент он работает для одной строки ввода, причем одна строка содержит искомый символ.

Чтобы развернуть несколько совпадений в строке, вы должныta, условно перейти обратно к метке :a:

$ printf "test1:test2:test3\nbob3:bob2:fred2\n"  | \
    sed -En ':a s/:/\n/g;/^[^\n]*2[^\n]*(\n|$)/P;D;ta'
test2
bob2
fred2
0 голосов
/ 27 июня 2019

Это просто НЕ работа для седа. С GNU awk для нескольких символов RS:

$ echo "test1:test2:test3:test4:test5:test6"| awk -v RS='[:\n]' '/1/'
test1

$ echo "test1:test2:test3:test4:test5:test6"| awk -v RS='[:\n]' 'NR%2'
test1
test3
test5

$ echo "test1:test2:test3:test4:test5:test6"| awk -v RS='[:\n]' '!(NR%2)'
test2
test4
test6

$ echo "foo1:bar1:foo2:bar2:foo3:bar3" | awk -v RS='[:\n]' '/foo/ || /2/'
foo1
foo2
bar2
foo3

С любым awk вам нужно будет удалить \n из окончательной записи перед тем, как работать с ним:

$ echo "test1:test2:test3:test4:test5:test6"| awk -v RS=':' '{sub(/\n$/,"")} /1/'
test1
...