Мне нужно взять сумму поля, которое разделено тильдой (~). Проблема в том, что мои данные также имеют экранированный разделитель.
Пример
1~CEO~ashok\~kumar~1000
Как мы видим в 3-м поле выше, мы экранировали разделитель, которого я хочу избежать. Я запускаю команду ниже, которая не обрабатывает это.
$ cat test.out|awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
Предположим, что test.out
данные следующие:
1~CEO~ashok\~kumar~1000
2~CFO~Ranjan~2000
3~CEO~kumar~1000
Таким образом, мой вывод должен быть 4000. Но в настоящее время с моей командой я получаю только 3000!
решение1
Просто измените экранированный разделитель на что-то другое перед обработкой с помощью awk
. Это можно сделать с помощью sed
:
$ cat test.out| sed 's/\\~/=/g' | \
awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
4000
И, как это часто бывает, это cat
не нужно:
$ sed 's/\\~/=/g' test.out | awk -F'~' 'BEGIN {sum=0} {sum+=$4} END{print sum}'
решение2
Вот альтернатива, которая не использует awk
:
$ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+ | bc
4000
Вышеуказанное используется sed
для замены экранированной тильды \~
в 3-м столбце. После этого мы можем использовать cut
для выбора 4-го столбца чисел, а затем реконструировать их так, чтобы они были разделены знаками плюс, ( +
).
$ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+
1000+2000+1000
Затем эта строка передается двоичному калькулятору, bc
который их суммирует.
решение3
Чтобы справиться с экранированием, общий метод заключается в использовании perl
или PCRE и их оператора чередования regexp в сочетании с оператором no-backtrack. Здесь с GNU grep
:
grep -Po '(?>(?:\\.|.)*?~){3}\K(?:\\.|[^~])*' << \EOF
1~CEO~ashok\~kumar~1000
2~CFO~Ranjan~2000
3~CEO~kumar~1000
4~field2~field3\\~10000~field5-note-the-escaped-backslash-not-tilde
5~a\~b\~c\~no-4th-field-here
EOF
который дает:
1000
2000
1000
10000
(которые вы можете суммировать с вашим обычным awk '{s+=$0};END{print s}'
).
С помощью GNU sed
вы также можете сделать это с помощью:
sed -rn 's/((\\.|[^\~])*~){3}((\\.|[^~])*).*/\3/p'
С помощью GNU awk
вы можете FPAT
определить поля как последовательности экранированных символов или символов, отличных от тильды и обратной косой черты:
awk -v FPAT='(\\\\.|[^\\\\~])*' '{print $4}'
решение4
Это немного неуклюже в awk (если только вы не можете предварительно обработать свой исходный код, чтобы изменить разделитель, но для этого требуется знать другой символ или последовательность символов, которые не могут встречаться во входных данных). Одна вещь, которую вы можете сделать, это прочитать целую строку, а затем обработать ее, чтобы получить символы новой строки в качестве разделителей (символы новой строки — это единственное, что не может встречаться в строке).
awk 'BEGIN {FS="\n"}
{
gsub("~", "\n");
gsub("\\\n", "~");
gsub("\\\\", "\\");
$0 = $0;
print $4;
}'