Obtendo substrings entre delimitadores várias vezes de uma string sem loop

Obtendo substrings entre delimitadores várias vezes de uma string sem loop

Procurei por toda parte uma solução para isso, mas não consegui encontrar uma.

Estou usando qstat -xpara passar uma grande sequência de informações de trabalho. A saída de qstat -xestá no formato XML. As substrings que procuro residem entre dois delimitadores explícitos <Output_Path>e </Output_Path>. Aqui está um exemplo de alguns resultados de qstat -x, com informações confidenciais censuradas:

<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>

Quero obter todas as substrings que residem entre cada iteração de <Output_Path>e </Output_Path>. Isto é, se eu tivesse a corda

<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>

Eu gostaria de um comando que retornasse

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

ou

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

Mas preciso que isso seja feito sem usar forloops lentos. Tentei usar variações de awk, grep, e sed, mas não consegui encontrar nada que funcionasse.

Alguma ideia?

Responder1

Tente isso, então:

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

Responder2

Se o grep do seu sistema suportar PCRE, talvez você possa fazer

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

ou se você precisar lidar com quantidades mais gerais de espaços em branco ao redor

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

Responder3

Se você concorda com algo estruturado como:

string1
string2
string3

Eu simplesmente substituiria seus delimitadores por uma nova linha. Algo como isto deve aproximar você:

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

Editar

Conforme apontado por @clk abaixo, minha primeira resposta pode fornecer novas linhas duplas. Mudando para algo como:

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

para mim rende:

 string1 string2 string3

que também funciona da mesma forma quando conectado, como:

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

Não é muitobonitoresposta, mas rápida e suja e fornece o formato que você está solicitando.

Responder4

Usando apenas sed (com -rsinalizador para regex estendido)

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

Devoluções

string1 string2 string3 

Você também tem esta versão usando tr e grep (com -vE):

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

Devoluções

string1
string2
string3

trapenas substitui o caractere de espaço por uma nova linha. grep -vEusa regex ("E") e exclui as linhas correspondentes ("v").

A terceira versão usa sed (sem flag) e grep (igual à última versão):

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

Fazendo exatamente a mesma coisa que a versão dois, usando sed em vez de tr.

Editar: também foi adicionado ^$na string de pesquisa grep para garantir que ela não retorne novas linhas indesejadas.

Edit2: Vejo que você mudou o OP. A resposta acima é para a pergunta original. Abaixo fiz um script que pode te ajudar: http://pastebin.com/uKWAGE0Y

informação relacionada