Заменить n-е -из -конечное вхождение строки в каждой строке

об этих строках:

iptables -A INPUT -p tcp --dport 8090 -j DROP
iptables -A INPUT -p tcp --dport 8090 -s <IP> -j ACCEPT

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

относительно NAT части вашего вопроса, неясно, однако в докере вы должны назначать порт при создании контейнера:

docker run... -p 8090:8080... 
8
24.06.2021, 11:16
7 ответов

Вы можете явно захватить еще два поля с разделителями:

$ sed -r 's/(.*);([^;]*;[^;]*;)/\1,\2/' input_file.csv
1;2;3;4;5;6,7;8;9
10;20;30;40;50;60,70;80;90
100;200;300;400;500;600,700;800;900

или более программно

$ sed -r 's/(.*);(([^;]*;){2})/\1,\2/' input_file.csv
1;2;3;4;5;6,7;8;9
10;20;30;40;50;60,70;80;90
100;200;300;400;500;600,700;800;900

где число в квантификаторе {n-1}заменяет n-е от последнего.

12
28.07.2021, 11:22

Вы можете просто поменять местами строки, изменить N-е вхождение, а затем повторно -поменять местами:

 rev file | sed 's/;/,/3' | rev
13
28.07.2021, 11:22

sedхорош, когда вы работаете со строками целиком, но awkи perlлучше, когда вам нужно работать с полями/столбцами внутри строки.

$ perl -F';' -lane 'splice @F,-4,2,join(",",@F[-4..-3]); print join(";",@F)' input.csv 
1;2;3;4;5;6,7;8;9
10;20;30;40;50;60,70;80;90
100;200;300;400;500;600,700;800;900

Это разбивает строку на массив @F, используя ;в качестве разделителя. Затем он заменяет четвертое -последнее и третье -последнее поля одной строкой, содержащей значения обоих полей, разделенных запятой.

Затем он печатает весь массив @F с запятой -в качестве разделителя полей.


splice— встроенная в Perl -функция манипулирования массивами общего назначения. Есть несколько форм его использования, но я использую здесь форму:

splice ARRAY,OFFSET,LENGTH,LIST

т.е.

splice @F, -4, 2, join(",",@F[-4..-3])
  • ARRAY:@F, массив Perl по умолчанию при использовании с параметрами -Fили -a, аналогичный $1, $2, awkНомера полей за 3 доллара.
  • OFFSET:-4, четвертое последнее поле
  • LENGTH:2, два поля (, то есть $F[-4]и$F[-3])
  • LIST:в этом случае "СПИСОК" имеет только один элемент, строку, содержащую значения полей -4 и -3, соединенные запятой. Однако в общем случае LIST может быть массивом любой длины, поэтому spliceтакже можно использовать для вставки дополнительных полей в массив.

Подробнее см. perldoc -f splice.

Кроме того, @F[-4..-3]— это то, что в Perl известно как срез массива. Оператор диапазона ..возвращает элементы полей от -4 до-3. Для этого скрипта его также можно было бы записать как @F[-4,-3], то есть элементы -4 . ] и-3. Срезы массива могут быть указаны с помощью диапазонов и/или списков.

6
28.07.2021, 11:22

Использованиеgawk:

awk -F';' '{print gensub(";", ",", NF-3)}'

gawk, построенный -в функции gensub(), заменяет точку с запятой от n до последней запятой.

8
28.07.2021, 11:22

С помощью GNU sedмы можем сделать следующее.

  • пропустить дальнейшую обработку, напечатать строку, вернуться к следующей строке, если в строке менее 3 точек с запятой.
  • заменить все точки с запятой на новые строки.
  • постепенно возвращать новые строки одну за другой, пока у нас не останется 3 новых строки.
  • Теперь измените первую новую строку на запятую, а остальные верните.
sed -e '
  s/;/&/3;T
  y/;/\n/

  :a;s/\n/&/4;tb
   s//,/;y/\n/;/
  :b;s//;/;ta
' file

1;2;3;4;5;6,7;8;9
10;20;30;40;50;60,70;80;90
100;200;300;400;500;600,700;800;900

С помощью Perlмы разделяем, а также записываем разделители полей в разделенный массив (@F ). Тогда 6-е поле с конца - это 3-я точка с запятой с конца, которую мы меняем на запятую. Остальное без изменений.

perl -aF'(;)' -plse '
  $F[-6]=",",s/.*/@F/ if @F >= 6;
' -- -\"=./file

Используя POSIX awk, мы изменяем разделители в зависимости от их положения:

awk -F\; -v n=3 '{ for(i=1; i<=NF; i++) printf "%s%s", $i, (i==NF-n?",":(i==NF?ORS:FS)) }' infile
2
28.07.2021, 11:22

Вариант ответа steeldriver , который закрепляет шаблон в конце строки и, следовательно, может использовать3(или любой другой номер, n, вам нужно использовать ), а не2(т.е.n-1)для подсчета повторов конечных полей, если на самом деле существует столько полей. Его также не волнуют начальные данные в строке перед разделителем, который мы хотим заменить.

Использование стандартаsed:

sed 's/;\(\([^;]*;\{0,1\}\)\{3\}\)$/,\1/' file

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

sed -E 's/;(([^;]*;?){3})$/,\1/' file

Любое выражение соответствует последним трем полям. Вложенное выражение -([^;]*;?)будет соответствовать строке, которая не содержит символа ;, а затем может заканчиваться одним таким символом. Последнее поле в строке не заканчивается на ;, так что это позволяет нам сопоставить это поле и по-прежнему привязывать полное выражение к концу строки.

Это, как и большинство sedрешений, не будет работать, если какое-либо поле содержит встроенные;символы или символы новой строки (, которые разрешены в CSV-файлах ).


Использование синтаксического анализатора CSV для преобразования данных в формат JSON, их изменение с помощью jqи последующее преобразование обратно в файл CSV с правильными разделителями. Это справится с данными, содержащими встроенные разделители, символы новой строки и кавычки :

.
csvjson -H -d ';' file |
jq -r --argjson n 3 '.[] | [.[]] |.[-($n+1):(if $n > 1 then -($n-1) else null end)]] |= [join(",")] | @csv' |
csvformat -H -D ';'

Здесь используется csvkitдля преобразования CSV в JSON. Затем он импортирует значение 3в переменную jqnв командной строке и использует ее для объединения двух полей на основе этого значения. Измененные данные затем возвращаются обратно в CSV и переформатируются для использования ;в качестве разделителей с самого начала.

Учитывая файл CSV с четырьмя столбцами и четырьмя строками,нравится

A;B;C;D
1;"2.1;2.2";3;4
1;2;"3.1
3.2
3.3";4
1;"And then I said ""2 2 2""";3;4

второй разделитель с конца заменяется на запятую (эффективно объединяет 2-е и 3-е поля в одно поле):

$ csvjson -H -d ';' file | jq -r --argjson n 2 '.[] | [.[]] |.[-($n+1):(if $n > 1 then -($n-1) else null end)] |= [join(",")] | @csv' | csvformat -D ';'
A;B,C;D
1;"2.1;2.2,3";4
1;"2,3.1
3.2
3.3";4
1;"And then I said ""2 2 2"",3";4
0
28.07.2021, 11:22
sed -E 's/;([^;]*(;[^;]*){2})$/,\1/' file

Описание

Если вам нужно начать с конца , используйте конец($)в качестве привязки для регулярного выражения.

Перед концом нам нужно сопоставить некоторые не-;символы, которым предшествует;:

;[^;]*$

Если мы повторим это три раза, мы доберемся до 3-го до последнего;:

;[^;]*;[^;]*;[^;]*$

Затем мы можем зафиксировать то, что нужно заменить:

s/;\([^;]*;[^;]*;[^;]*\)$/,\1/

Или, сделав количество повторяющихся секций числом(n-1):

sed 's/;\([^;]*\(;[^;]*\)\{2\}\)$/,\1/' file

Или, чтобы удалить кратное \, используйте расширенное регулярное выражение:

sed -E 's/;([^;]*(;[^;]*){2})$/,\1/' file
-1
28.07.2021, 11:22

Теги

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