Intereting Posts
Как найти идентификатор пользователя после входа в систему как root, используя pmksh? Сохранение и восстановление полного образа диска Как использовать awk или sed для преобразования строк в столбцы Как использовать пользовательскую переменную в команде для создания условия в if, если Почему разные гиперпотоки имеют разную тактовую частоту? zip-файлы в каталоге на основе шаблона перед перемещением в место архива настройка параметров по умолчанию для cryptsetup Как восстановить привязки bash? Почему «while .. read .. << EOL» выполняет расширение переменной, но <файл и | не? Linux делает 1 большой из 3 меньших фотографий Почему ubuntu так медленно работает на SD-карте? Команда, чтобы заставить пользователя вводить пароль – RHEL / Centos Копировать файл при замене текста в нем dconf не работает при запуске приложения X от другого пользователя Протрите USB-накопитель и заново создайте файловую систему

Как я могу избежать экранированных символов разделителя в команде Unix?

Мне нужно взять сумму поля, которое ограничено тильдой (~). Проблема заключается в том, что у моих данных также имеется разделитель.

пример

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!

Просто измените экранированный разделитель на что-то еще до обработки с помощью 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}' 

Вот альтернатива, которая не использует awk :

 $ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+ | bc 4000 

Вышеупомянутое использует sed для замены экранированной тильды \~ в третьем столбце. Впоследствии мы можем использовать cut чтобы выбрать 4-й столбец чисел, а затем восстановить их так, чтобы они были разделены знаками плюс ( + ).

 $ sed 's/\\~/=/g' test.out | cut -d"~" -f4 | paste -sd+ 1000+2000+1000 

Затем эта строка передается двоичному калькулятору bc который суммирует их.

Чтобы справиться с экранированием, общий метод заключается в использовании perl или PCRE и оператора регулярного выражения с переменным в сочетании с оператором 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 для определения полей как последовательностей как экранированных символов, так и символов без FPAT :

 awk -v FPAT='(\\\\.|[^\\\\~])*' '{print $4}' 

Кажется, что вы хотите суммировать все значения в последнем столбце каждой строки. поэтому попробуйте следующее:

 $ awk -F'~' '{sum += $NF};END{print sum}' file 4000 

Если вы можете использовать perl :

 $ perl -F'~' -anle '$sum += $F[-1]; END {print $sum}' file 4000 

или:

 $ perl -nle '$sum += $1 if /(\d+$)/; END {print $sum}' file 4000 

Это немного неуклюже в awk (если вы не можете предварительно обработать ваш источник, чтобы изменить разделитель, но для этого требуется знание другого символа или последовательности символов, которые не могут появиться на входе). Одна вещь, которую вы можете сделать, это прочитать целую строку, а затем массировать линию, чтобы получить символы новой строки как разделители (новые строки – это одна вещь, которая не может появиться в строке).

 awk 'BEGIN {FS="\n"} { gsub("~", "\n"); gsub("\\\n", "~"); gsub("\\\\", "\\"); $0 = $0; print $4; }' 
 for n in $(cat <<\DATA 1~CEO~ashok\~kumar~1000 2~CFO~Ranjan~2000 3~CEO~kumar~1000 DATA ) do r=$((r+${n##*~})) ; done echo $r ###OUTPUT 4000 

Учитывая данные, которые вы показываете простым циклом for может сделать, но если есть вероятность использования <tab> или <space> в данных, которые вы можете ограничить расщеплением, например:

 IFS=' ' 

…первый.

Или с sed и bc :

 sed 's/.*[^0-9]/r+=/;$aprint r' <<\DATA | bc 1~CEO~ashok\~kumar~1000 2~CFO~Ranjan~2000 3~CEO~kumar~1000 DATA 

ВЫВОД:

 4000 

Оба метода полагаются на ваши целевые данные, являющиеся последним полем в каждой строке.

Первый делает простую оболочку итерабельным $(( арифметикой )) на $((r)) после удаления всего до и включая ваш последний разделитель от $n .

Второй заменяет все удаленные в первом методе строкой 'r+=' и |pipes r=r+${last_field} результат в bc а затем r=r+${last_field} . В последней строке ввода sed также a другая строка:

 print r 

… и |pipes его на bc который затем выполняет как указано и выводит, как показано.