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

2

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

Estou usando qstat -x para passar uma grande quantidade de informações sobre o trabalho. A saída de qstat -x está no formato XML. As subseqüências que estou procurando 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>

Eu quero obter todas as subseqüências que residem entre cada iteração de <Output_Path> e </Output_Path> . Isto é, se eu tivesse a string

<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 quero um comando que retorne

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 eu preciso disso sem usar nenhum loop for lento. Eu tentei usar variações em awk , grep e sed , mas não consegui encontrar nada que funcionasse.

Alguma idéia?

    
por boof 24.06.2016 / 17:21

5 respostas

2

Tente isso, então:

xmlstarlet sel -t -v //Output_Path -nl data.xml
    
por 29.06.2016 / 08:05
1

Se o grep do seu sistema suportar o 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

$ echo 'aaa string1 bbb aaa string2 bbb aaa string3 bbb' |
  grep -oP '(aaa|bbb)\s+\K\w*?(?=\s+(aaa|bbb))'
string1
string2
string3
    
por 24.06.2016 / 17:56
0

Se você está bem com algo estruturado como:

string1
string2
string3

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

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

Editar

Como apontado por @clk abaixo, minha primeira resposta pode dar duas linhas novas. Mudando para algo como:

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

para mim produz:

 string1 string2 string3

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

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

Não muito bonita resposta, mas rápida e suja e dá-lhe o formato que você está pedindo.

    
por 24.06.2016 / 17:30
0

Para analisar XML, use um analisador de XML.

O XMLStarlet é um analisador XML de linha de comando que é muito bom para esse tipo de situação.

Assumindo que o seu XML está completo (faltando </Job></Data> no final, como está escrito agora), então você pode extrair o valor do nó Output_Path com

$ xml --template --value-of '//Output_Path' -nl input.xml
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

O --template especifica que estamos procurando o valor do nó nomeado em qualquer lugar no documento de entrada. O -nl no final faz com que o XMLStarlet imprima uma nova linha após os últimos dados.

Você também pode canalizar para o XMLStarlet:

$ yourcommand | xml sel ...
    
por 11.07.2016 / 14:18
-1

Usando somente sed (com -r sinalizador para regex estendido)

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

Retorna

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|^$)'

Retorna

string1
string2
string3

tr apenas substitui o caractere de espaço por uma nova linha. grep -vE usa 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 praticamente a mesma coisa que a versão dois, usando sed em vez de tr.

Editar: também adicionamos o ^$ na string de pesquisa do grep para garantir que ele não retorne novas linhas indesejadas.

Edit2: vejo que você mudou o OP. A resposta acima é para a pergunta original. Abaixo eu fiz um script que pode te ajudar: link

    
por 24.06.2016 / 18:19

Tags