Получение подстрок между разделителями несколько раз из одной строки без зацикливания

Получение подстрок между разделителями несколько раз из одной строки без зацикливания

Я искал решение этой проблемы везде, но так и не нашел.

Я использую qstat -xдля передачи большой строки информации о работе. Вывод из qstat -xпредставлен в формате XML. Подстроки, которые я ищу, находятся между двумя явными разделителями <Output_Path>и </Output_Path>. Вот пример части вывода из qstat -x, в котором конфиденциальная информация отцензурирована:

<Data><Job><Job_Id>4382.xxxxxxxx.xx.xxxxxxx</Job_Id><Job_Name>r053_x.xxMx.xxR_400k_neos2.pbs</Job_Name><Job_Owner>[email protected]</Job_Owner><job_state>H</job_state><queue>default</queue><server>xxxxxxxx.xx.xxxxxxx</server><Checkpoint>u</Checkpoint><ctime>1466396941</ctime><Error_Path>xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r053_x.xxMx.xxR_400k_neos2/r053_x.xxMx.xxR_400k_neos2.pbs.e4382</Error_Path><Hold_Types>u</Hold_Types><Join_Path>n</Join_Path><Keep_Files>n</Keep_Files><Mail_Points>a</Mail_Points><mtime>1466423857</mtime><Output_Path>xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r053_x.xxMx.xxR_400k_neos2/r053_x.xxMx.xxR_400k_neos2.pbs.o4382</Output_Path><Priority>0</Priority><qtime>1466396941</qtime><Rerunable>True</Rerunable><Resource_List><cput>9999:59:59</cput><nodect>1</nodect><nodes>1:ppn=12:gpus=1</nodes><walltime>2400:00:00</walltime></Resource_List><comment>Not Running: Not enough of the right type of nodes are available</comment><submit_args>r053_x.xxMx.xxR_400k_neos2.pbs</submit_args><fault_tolerant>False</fault_tolerant><job_radix>0</job_radix><submit_host>xxxxxxxx.xx.xxxxxxx</submit_host></Job><Job><Job_Id>4396.xxxxxxxx.xx.xxxxxxx</Job_Id><Job_Name>0R_20k_neos2.pbs</Job_Name><Job_Owner>[email protected]</Job_Owner><job_state>H</job_state><queue>default</queue><server>xxxxxxxx.xx.xxxxxxx</server><Checkpoint>u</Checkpoint><ctime>1466606895</ctime><Error_Path>xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r061_x.xxMx.xxR_20k_neos2/0R_20k_neos2.pbs.e4396</Error_Path><Hold_Types>u</Hold_Types><Join_Path>n</Join_Path><Keep_Files>n</Keep_Files><Mail_Points>a</Mail_Points><mtime>1466609370</mtime><Output_Path>xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r061_x.xxMx.xxR_20k_neos2/0R_20k_neos2.pbs.o4396</Output_Path><Priority>0</Priority>

Я хочу получить все подстроки, которые находятся между каждой итерацией <Output_Path>и </Output_Path>. То есть, если бы у меня была строка

<Output_Path>xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r061_x.xxMx.xxR_20k_neos2/0R_20k_neos2.pbs.o4396</Output_Path><Output_Path>xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r053_x.xxMx.xxR_400k_neos2/r053_x.xxMx.xxR_400k_neos2.pbs.o4382</Output_Path>

Мне нужна команда, которая возвращает

xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r061_x.xxMx.xxR_20k_neos2/0R_20k_neos2.pbs.o4396
xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r053_x.xxMx.xxR_400k_neos2/r053_x.xxMx.xxR_400k_neos2.pbs.o4382

или

xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r061_x.xxMx.xxR_20k_neos2/0R_20k_neos2.pbs.o4396 xxxxxxxx.xx.xxxxxxx:/data/xxxxxxxx/xxxxxxxxxxx/summer2016/relax/r053_x.xxMx.xxR_400k_neos2/r053_x.xxMx.xxR_400k_neos2.pbs.o4382

Но мне нужно сделать это без использования медленных forциклов. Я пробовал использовать вариации на awk, grep, и sed, но не смог найти ничего, что работало бы.

Есть идеи?

решение1

Тогда попробуйте это:

xmlstarlet sel -t -v //Output_Path -nl data.xml

решение2

Если grep вашей системы поддерживает PCRE, вы можете сделать следующее:

$ echo 'aaa string1 bbb aaa string2 bbb aaa string3 bbb' | 
  grep -oP '(?<=(aaa|bbb) )\w*?(?= (aaa|bbb))'
string1
string2
string3

или если вам нужно обработать более общие объемы окружающего пробела

$ echo 'aaa string1 bbb aaa string2 bbb aaa string3 bbb' |
  grep -oP '(aaa|bbb)\s+\K\w*?(?=\s+(aaa|bbb))'
string1
string2
string3

решение3

Если вас устроит что-то структурированное вроде:

string1
string2
string3

Я бы просто заменил ваши разделители на новую строку. Что-то вроде этого должно приблизить вас к этому:

sed "s/\(aaa\)\|\(bbb\)/\n/g" test.txt

Редактировать

Как указал @clk ниже, мой первый ответ может дать двойные переводы строк. Изменим на что-то вроде:

sed "s/\(\s\)\?aaa\(\s\)\?/bbb/g" test.txt | sed "s/b*//g"

для меня выход:

 string1 string2 string3

который также работает так же при передаче по каналу, например:

echo 'aaa string1 bbb aaa string2 bbb aaa string3 bbb' | sed "s/\(\s\)\?aaa\(\s\)\?/bbb/g" | sed "s/b*//g"

Не оченьсимпатичныйответ, но быстрый и грубый, и дает вам тот формат, который вы запрашиваете.

решение4

Используя только sed (с -rфлагом для расширенного регулярного выражения)

echo "aaa string1 bbb aaa string2 bbb aaa string3 bbb" | sed -r 's/(aaa|bbb) ?//g'

Возвраты

string1 string2 string3 

У вас также есть эта версия, использующая tr и grep (с -vE):

echo "aaa string1 bbb aaa string2 bbb aaa string3 bbb" | tr ' ' '\n'| grep -vE '(aaa|bbb|^$)'

Возвраты

string1
string2
string3

trпросто заменяет символ пробела на новую строку. grep -vEиспользует регулярное выражение ("E") и исключает совпадающие строки ("v").

Третья версия использует sed (без флага) и grep (то же, что и предыдущая версия):

echo "aaa string1 bbb aaa string2 bbb aaa string3 bbb" | sed 's/\s/\n/g' | grep -vE '(aaa|bbb|^$)'

Делает практически то же самое, что и версия 2, используя sed вместо tr.

Редактировать: Также добавил ^$в строку поиска grep, чтобы убедиться, что она не возвращает нежелательные символы новой строки.

Edit2: Я вижу, что вы сменили OP. Выше ответ на исходный вопрос. Ниже я сделал скрипт, который может вам помочь: http://pastebin.com/uKWAGE0Y

Связанный контент