Как получить все номера из строки и добавить их?

Мне нужно проанализировать XML-файл, сгенерированный для суммирования результатов запуска testSuite на каком-то программном обеспечении. В строке у меня есть, например:

<Summary failed="10" notExecuted="0" timeout="0" pass="18065" /> 

Это указывает на то, что количество тестов не выполнено, не выполнено и прошло. Мне нужно выяснить, сколько тестов было в тестовом комплекте, поэтому мне нужно добавить, в приведенном выше примере, 10 + 0 + 18065 = 18075.

Как я могу сделать это в Bash?

Просто выбросьте все символы, которые не являются цифрами, а не пробелом:

 echo '<Summary failed="10" notExecuted="0" timeout="0" pass="18065" />'|\ sed -e 's/[^0-9 ]//g' 

дает

 10 0 0 18065 

,

Сумма может быть сделана с помощью dc (при этом поле тайм-аута фильтруется по запросу)

 echo '<Summary failed="10" notExecuted="0" timeout="0" pass="18065" />'|\ sed -e 's/timeout="[0-9]*" //' \ -e 's/[^0-9 ]//g' \ -e 's/^ *//' \ -e 's/ *$//' \ -e 's/ /+/g' \ -e 's/^/0 /' \ -e 's/$/pq/'|dc 

,

Описание

В качестве сценария sed это будет выглядеть так

 s/timeout="[0-9]*" // #remove the timeout s/[^0-9 ]//g #drop anything but numbers and spaces s/^ *// #drop spaces at the beginning of the line s/ *$// #drop spaces at the end of the line s/ /+/g #replace remaining spaces with + s/^/0 / #add a 0 to initialize the sum for dc s/$/pq/ #add print and quit command for dc 

Сценарий можно просто использовать с

 INPUT|sed -f script.sed 

, Я оставляю для вас применение этого сценария с sed и dc для многострочного ввода. Тот, который я написал, работает только на одной линии!

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

 perl -lne 'my @a=$_=~/(\d+)/g;$sum+=$_ for @a; print $sum' file 

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

 tr ' ' '\n' < file | awk '/[0-9]+/ {gsub(/[^0-9]/, "", $0); sum+=$0} END {print sum}' 

пример

 % perl -lne 'my @a=$_=~/(\d+)/g;$sum+=$_ for @a; print $sum' foo 18075 % tr ' ' '\n' < foo | awk '/[0-9]+/ {gsub(/[^0-9]/, "", $0); sum+=$0} END {print sum}' 18075 % cat foo <Summary failed="10" notExecuted="0" timeout="0" pass="18065" /> 

Вы можете использовать xmlstarlet для правильного разбора xml.

Для вашей проблемы:

 total=0; \ for i in failed notExecuted pass; do \ sum=`xmlstarlet sel -t -v "//Summary/@$i" test.xml`; \ total=$(($sum + $total)); \ done; \ echo "Total=$total" 

где test.xml – это ваш файл, содержащий данные xml.

Вот еще один с dc :

 { tr -cs 0-9 \\n echo '[pq]sq[z2>q+l+x]s+l+x' } <<\IN | dc <Summary failed="10" notExecuted="0" timeout="0" pass="18065" /> IN 

dc сначала читает во всей infile – после того, как tr сжимает каждую последовательность символов not-number в единую \n строку, а затем читает в маленькой строке макроса echo цикла, которая сообщает ему добавлять все значения в свой стек каждый следующий, пока осталось меньше двух, и в этот момент он просто печатает все, что было, и уходит. В этом случае сумма …


 18075 

Если у вас есть GNU dc вы можете написать его так:

 tr -cs 0-9 \\n <in | dc -f- -e'[pq]sq[z2>q+l+x]s+l+x' 

Или, если infile очень большой, вы также захотите его заблокировать, чтобы поддерживать dc буферизацию слишком сильно в своем стеке.

 (tr -cs 0-9 \\n|xargs -n128|tr \ +)<in | dc -e'[pq]sq' -e'0[?z2>q+l+x]s+l+x' 

… который будет буферизовать 128 номеров за раз.

Как это:

 seq -skfkridmdk 100000 | (tr -cs 0-9 \\n|xargs -n128|tr \ +)| dc -e'[pq]sq' -e'0[?z2>q+l+x]s+l+x' 

 5000050000 

Если вы уверены в том, что по четыре штуки в строке, и вы хотите сбросить *timeout=* тогда вы можете сделать:

 <in grep '^<Summary' | cut -d\" -f2,4,8 | tr \" \\n | xargs -n512 | tr \ + | dc -e'[?z2>q+l+x]s+'\ -e\[pq]sq -e0l+x 

Которое будет терпеть неудачу, notExecuted, пройти, пока их " относительные позиции являются постоянными, и это единственные типы строк ввода, которые могут совпадать с« ^<Summary . Я пробовал:

 for x in 512 4096 16384; do time \ yes $'kdkeifndjei\n<Summary failed="10" notExecuted="0" timeout="0" pass="18065" />'| grep '^<Summary' | cut -d\" -f2,4,8 | head -n1000000 | tr \" \\n | xargs -n"$x" | tr \ + | dc -e'[?z2>q+l+x]s+'\ -e\[pq]sq -e0l+x done 

… для 3-х унций 3 мил значения кусок, и результаты были:

 18075000000 4.00s user 0.04s system 72% cpu 5.549 total 18075000000 2.82s user 0.01s system 99% cpu 2.831 total 18075000000 2.67s user 0.01s system 99% cpu 2.680 total 

Вариант одного из @ikrabbe. Подумайте, что это должно работать: echo '<Summary failed="10" notExecuted="0" timeout="0" pass="18065" />'| sed -e 's/[^0-9 ]//g' | cut -d" " -f2,3,5 | tr " " "+" | bc echo '<Summary failed="10" notExecuted="0" timeout="0" pass="18065" />'| sed -e 's/[^0-9 ]//g' | cut -d" " -f2,3,5 | tr " " "+" | bc echo '<Summary failed="10" notExecuted="0" timeout="0" pass="18065" />'| sed -e 's/[^0-9 ]//g' | cut -d" " -f2,3,5 | tr " " "+" | bc Поля начинаются с 2, так как в первой позиции есть пробел.