Как заменить mth на nth вхождение строки в bash с помощью sed? - PullRequest
1 голос
/ 01 мая 2020

Я искал простой способ замаскировать (т.е. заменить на *) с 1-го по 3-е вхождение данного шаблона в каждой строке , используя sed .

Формат ввода текста:

$ cat input
1234 4321 2356 7890
3456 4567 8765 0981
2345 2167 9876 1234

Требуемый вывод;

**** **** **** 7890
**** **** **** 0981
**** **** **** 1234

Осмотревшись, я нашел способ заменить только n-м вхождением указанного шаблона. Вот как вы заменяете 2-й блок каждой строки в данном входе.

$ cat input | sed  's/[0-9]\{4\}/****/2'
1234 **** 2356 7890
3456 **** 8765 0981
2345 **** 9876 1234

Также мы можем заменить все вхождения данного шаблона , начиная с n'th вхождение. Вот как вы это делаете.

$ cat input | sed  's/[0-9]\{4\}/****/2g'
1234 **** **** ****
3456 **** **** ****
2345 **** **** ****

PS Я был бы признателен за простое и глупое решение. Не решения с awk или ярлыками и петлями в sed .

Ответы [ 3 ]

1 голос
/ 01 мая 2020

«Простой» и «глупый», спросите вы. Я оставлю вас, чтобы решить, как это;). GNU sed поддерживает шаблон для запуска замены с sed /../[n]g, но не разрешает диапазон останова. Вы можете упомянуть 2g, чтобы начать замену со 2-го вхождения, но не можете определить диапазон, как 1-е-3-е вхождение.

sed -e 's/[0-9]\{4\}/****/; s/[0-9]\{4\}/****/; s/[0-9]\{4\}/****/' file

Но отмена awk для такой тривиальной замены, как это, не хорошая идея. Это гораздо полезнее и проще в использовании, чем вы думаете.

1 голос
/ 01 мая 2020

С вашим простым примером ввода:

sed -r 's/([0-9]{4} ){3}/**** **** **** /' input

Более сложное решение дано в https://unix.stackexchange.com/a/155810/57293.

Еще одно простое решение (если вы знаете количество шаблонов в строке):

rev input | sed -r 's/[0-9]{4}/****/g2' | rev
1 голос
/ 01 мая 2020

1-е решение: ИМХО, если вы спросите меня простое решение, то я бы go для awk. Если ваш Input_file содержит только 4 поля, просто сделайте это, просто присвойте значения 3 полям.

awk '{$1=$2=$3="****"} 1'  Input_file

2-е решение: С sed (это может быть так, как ОП пытался написать). Использование возможности sed использовать временный буфер для хранения соответствующего регулярного выражения, а затем заменить его на **** при замене.

sed 's/\([^ ]*\) \([^ ]*\) \([^ ]*\) \(.*\)/**** **** **** \4/'  Input_file

3-е решение: Используйте rev, чтобы напечатать обратный файл Input_file, затем сначала поймать (что на самом деле является последним полем в файле Input_file), а затем вывести 3 раза **** и снова распечатать его в обратном порядке, который теперь будет печататься в его фактической форме:)

rev Input_file | sed 's/\([^ ]*\).*/\1 **** **** **** ****/' | rev

4-е решение: Более универсальное c решение, в котором сотрудник может указать диапазон номеров полей, из какого поля, в каком поле пользователь хочет изменить значение на ****, затем попробуйте следующее (from и to - переменные, которые могут быть установлены человеком для изменения значений в соответствии с номерами полей).

awk -v from="1" -v to="3" '{for(i=from;i<=to;i++){$i="****"}} 1' Input_file
...