Obtener subcadenas entre delimitadores varias veces desde una cadena sin bucles

Obtener subcadenas entre delimitadores varias veces desde una cadena sin bucles

He buscado por todas partes una solución a esto, pero no pude encontrarla.

Estoy usando qstat -xpara pasar una gran cadena de información laboral. La salida qstat -xestá en formato XML. Las subcadenas que busco residen entre dos delimitadores explícitos <Output_Path>y </Output_Path>. A continuación se muestra un ejemplo de algunos de los resultados de qstat -x, con información confidencial censurada:

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

Quiero obtener todas las subcadenas que residen entre cada iteración de <Output_Path>y </Output_Path>. Es decir, si tuviera la cuerda

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

Me gustaría un comando que regrese

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

o

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

Pero necesito hacerlo sin utilizar forbucles lentos. Intenté usar variaciones de awk, grepy sed, pero no pude encontrar nada que funcionara.

¿Algunas ideas?

Respuesta1

Prueba esto, entonces:

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

Respuesta2

Si el grep de su sistema admite PCRE, tal vez podría hacerlo

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

o si necesita manejar cantidades más generales de espacios en blanco circundantes

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

Respuesta3

Si estás de acuerdo con algo estructurado como:

string1
string2
string3

Simplemente reemplazaría sus delimitadores con una nueva línea. Algo como esto debería acercarte:

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

Editar

Como señala @clk a continuación, mi primera respuesta puede dar nuevas líneas dobles. Cambiar a algo como:

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

para mí produce:

 string1 string2 string3

que también funciona igual cuando se conecta, como por ejemplo:

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

No es muybonitoResponde, pero es rápido y sucio y te da el formato que estás pidiendo.

Respuesta4

Usando solo sed (con -rbandera para expresiones regulares extendidas)

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

Devoluciones

string1 string2 string3 

También tienes esta versión usando tr y grep (con -vE):

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

Devoluciones

string1
string2
string3

trsimplemente reemplaza el carácter de espacio con una nueva línea. grep -vEutiliza expresiones regulares ("E") y excluye las líneas coincidentes ("v").

La tercera versión usa sed (sin bandera) y grep (igual que la última versión):

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

Haciendo prácticamente lo mismo que la versión dos, usando sed en lugar de tr.

Editar: también se agregó ^$en la cadena de búsqueda grep para asegurarse de que no devuelva nuevas líneas no deseadas.

Edit2: Veo que cambiaste el OP. La respuesta anterior es a la pregunta original. A continuación he creado un script que podría ayudarte: http://pastebin.com/uKWAGE0Y

información relacionada