GNU sed и новые строки с несколькими скриптами - PullRequest
0 голосов
/ 05 апреля 2020

Предположим, мы начинаем с этой строки:

echo "1:apple:fruit.2:banana:fruit.3:cucumber:veggie.4:date:fruit.5:eggplant:veggie.">list.tmp

и хотим получить такой результат:

1-apple:fruit
2-banana:fruit
3-cucumber:veggie
4-date:fruit
5-eggplant:veggie

Почему это работает:

sed -e 's/\./\n/g' -i list.tmp
sed -e 's/:/-/' list.tmp

Но не это:

sed -e 's/\./\n/g' -e 's/:/-/' list.tmp

Вторая команда возвращает это, по-видимому, игнорируя новые символы новой строки при поиске первого вхождения ':' в каждой строке.

1-apple:fruit
2:banana:fruit
3:cucumber:veggie
4:date:fruit
5:eggplant:veggie

С расширенной версией ввода:

echo "one:apple:fruit.two:banana:fruit.three:cucumber:veggie.four:date:fruit.five:eggplant:veggie.">list.tmp

Я хочу закончить с таким результатом:

one-apple:fruit
two-banana:fruit
three-cucumber:veggie
four-date:fruit
five-eggplant:veggie

Ответы [ 2 ]

2 голосов
/ 05 апреля 2020

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

sed -E 'y/./\n/;s/^([^:]*):/\1-/mg' file

Перевести все периоды на новые строки.

Используя спецификацию GNU c m или многострочный флаг, замените из начало каждой строки в пространстве шаблона (т. е. начало строки, обозначенной ^, является либо началом строки, либо последующим символом новой строки), любые символы, отличные от двоеточия, за которыми следуют двоеточие и символы, отличные от двоеточия. и да sh -. Это эффективно заменяет первое двоеточие в каждой строке на da sh.

2 голосов
/ 05 апреля 2020

Перевод ключевого комментария в ответ.

Исходные данные

Вы забыли модификатор g для второй команды в двойном -e формулировка. Когда первый -e завершается, все строки все еще находятся в пространстве образца (основная рабочая область в sed) - они не становятся 5 отдельно читаемыми строками. Вы читаете одну строку; Вы все еще обрабатываете это. Имейте в виду, вам нужно использовать измененный шаблон:

s/\([0-9]\):/\1-/g

Комбинируя их, в GNU sed (как указано в заголовке вопроса), вы получите:

sed -e 's/\./\n/g' -e 's/\([0-9]\):/\1-/g' list.tmp

Обратите внимание, что POSIX sed и другие версии sed имеют разные правила замены строки в первом выражении -e.

Рекомендуется использовать awk

Если вы меняете инструменты с От sed до awk - опция, вы можете сделать это более просто в awk, как показано Ed Morton в комментарии . Поскольку это решение не нуждается в изменении для обращения к пересмотренным данным, оно явно имеет преимущества - недостатком является то, что оно не использует sed. В «реальном мире» вы используете лучший инструмент для работы - и в этом примере это awk.

Расширенные данные

С «расширенным» вводом, где нет удобные одиночные числа di git, но вы хотите изменить первое двоеточие в каждой строке на da sh, вам нужно работать усерднее:

sed -e 's/\./\n/g' \
    -e  's/^\([^:]*\):/\1-/' \
    -e 's/\(\n[^:]*\):/\1-/g' \
    list.tmp
  • Первый -e в неизменном виде.
  • Второй ищет последовательность не-двоеточий, за которыми следует двоеточие в начале пространства шаблона, и заменяет его на последовательность не-двоеточий и да sh. Модификатор g здесь не имеет значения.
  • Третий -e ищет новую строку, за которой следует последовательность не-двоеточий, за которой следует двоеточие, и заменяет ее новой строкой, последовательностью без двоеточия и да sh. Модификатор g очень важен здесь.

Вы можете выровнять все это на одну строку, но легче увидеть сходство между двумя последними вариантами -e, если они выложены в отдельных строках.

Вы также можете поэкспериментировать с ERE (расширенными регулярными выражениями) с параметром -E и сгруппировать две отдельные замены в одну:

{
echo "1:apple:fruit.2:banana:fruit.3:cucumber:veggie.4:date:fruit.5:eggplant:veggie."
echo "one:apple:fruit.two:banana:fruit.three:cucumber:veggie.four:date:fruit.five:eggplant:veggie."
} |
sed -E -e 's/\./\
/g' -e 's/((^|\n)[^:]+):/\1-/g'

, что приводит к:

1-apple:fruit
2-banana:fruit
3-cucumber:veggie
4-date:fruit
5-eggplant:veggie

one-apple:fruit
two-banana:fruit
three-cucumber:veggie
four-date:fruit
five-eggplant:veggie

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

{
echo "1:apple:fruit.2:banana:fruit.3:cucumber:veggie.4:date:fruit.5:eggplant:veggie."
echo "one:apple:fruit.two:banana:fruit.three:cucumber:veggie.four:date:fruit.five:eggplant:veggie."
} |
sed -E -e 's/\./\
/g' -e 's/((^|\n)[^:]+):/\1-/g' -e 's/\n$//'

Нотация обратной строки sh с обратным символом работает правильно как в GNU sed, так и в POSIX (включая BSD и macOS) sed; вы можете заменить его на \n в GNU sed. \n в замещающей части команды s/// не работает в BSD (macOS) sed. POSIX sed требует, чтобы вы использовали обратную косую черту sh для экранирования буквального перехода на новую строку в тексте замены:

Строка может быть разбита путем подстановки <newline> в Это. Приложение должно экранировать <newline> в замене, предшествуя ему <backslash>.

GNU sed является более гибким.

Также (согласно potong ' answer ), существует спецификатор GNU c m, который можно использовать для выполнения нескольких операций. сопоставление строк за одну операцию.

...