Как использовать sed для редактирования после первого появления определенного символа? - PullRequest
0 голосов
/ 18 апреля 2020

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

sed 's/...//Ig; s/...//Ig; s/...//Ig'

Но я обнаружил, что допустил ошибку, я должен редактировать деталь только после первого появления : , Как изменить эту команду sed и / или использовать другие команды для ее достижения?

Строки для редактирования на самом деле выводят grep, например:

/foo/bar:foobar
/foobar/foo/bar:foo_bar

Ответы [ 5 ]

3 голосов
/ 18 апреля 2020

Для простоты я предполагаю, что вы хотите заменить каждое foo вхождение после первого : на FOO.

sed 'h           # save the current line in the hold space
     s/[^:]*://  # delete everything up to the marker
     s/foo/FOO/g # YOUR COMPLICATED COMMAND GOES HERE
     x           # swap pattern and hold space
     s/:.*/:/    # delete from the first : to the end in the original line
     G           # append hold space (: with whatever follows it)
     s/\n//' yourfile # remove the newline that comes with G

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

Оригинальный ответ ниже. Даже если в этом случае это немного излишне, это показывает, что вы можете использовать нулевой символ в sed, \x0, в качестве «маркера», который, как вы можете предположить, отсутствует в текстовом файле (в отличие от используя, например, _xxx_, который потенциально может быть уже в файле). (Эта вторая версия заменяет вхождения foo до :, в соответствии с тем, когда я неправильно прочитал вопрос. )

sed 'h           # save the current line in the hold space
     s/:/\x0:/   # mark the first : by prepending a null character
     s/.*\x0//   # delete everything up to the marker
     x           # swap pattern and hold space
     s/:.*//     # delete from the first : to the end in the original line
     s/foo/FOO/g # YOUR COMPLICATED COMMAND GOES HERE
     G           # append hold space (: with whatever follows it)
     s/\n//' yourfile # remove the newline that comes with G
2 голосов
/ 18 апреля 2020

awk может использоваться здесь как лучшая альтернатива:

awk 'BEGIN{FS=OFS=":"} {s=$1; $1=""; gsub(/a/, "@"); gsub(/o/, "0"); print s $0}' file

/foo/bar:f00b@r
/foobar/foo/bar:f00_b@r

Здесь мы разделяем ввод, используя : и сохраняя первое поле перед : в переменная s. Затем мы запускаем несколько функций gsub для выполнения всех подстановок и, наконец, печатаем сохраненную переменную с остальной частью строки.

1 голос
/ 19 апреля 2020

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

sed 's/:/\n&/;h;s/foo/FOO/g;s/bar/BAR/g;y/-/_/;H;g;s/\n.*\n//' file

Ввести символ новой строки перед первым :.

Скопировать результат в область удержания (HS).

Заменить / перевести глобально один или несколько раз.

Добавить пространство шаблона (PS) к HS.

Заменить PS на HS.

Удалите символы новой строки и все, что между ними.

1 голос
/ 18 апреля 2020

Сначала разбейте каждую строку вывода grep на 2 строки и выполните ваши sed команды на четных строках.
Это будет выглядеть как

grep "something" list_of_file |
  sed 's/:/\n/' |
  sed '0~2s/...//Ig; 0~2s/...//Ig; 0~2s/...//Ig' |
  paste -d":" - -

С 0~2 вы говорит sed работать только на четных линиях.
Пример:

grep -E "root|127" /etc/{passwd,hosts} |
   sed 's/:/\n/' |
   sed -r '0~2s/([0,o])/==\1==/g' | 
   paste -d":" - -

вывод:

/etc/passwd:r==o====o==t:x:==0==:==0==:r==o====o==t:/r==o====o==t:/bin/bash
/etc/hosts:127.==0==.==0==.1    l==o==calh==o==st
0 голосов
/ 26 апреля 2020

Вы можете попробовать Perl альтернативу. Вот одно решение, использующее позитивный взгляд

$ cat grep_out.dat
/foo/bar:foobar
/foobar/foo/bar:foo_bar
$ perl -pe ' s/(?<=:)(foo)/\U\1/g ' grep_out.dat
/foo/bar:FOObar
/foobar/foo/bar:FOO_bar
$
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...