Cómo dar una lista separada por comas como argumentos para el siguiente comando

9

Tengo un script s1que genera una lista de números separados con ',' por ejemplo 1,2,3,4. Ahora quiero dar estos números al script s2como argumentos, para que s2 se ejecute en cada uno de ellos y muestre su resultado en una línea separada. Por ejemplo, si s2 multiplica los números por dos, este sería el resultado que estoy buscando:

2
4
6
8

Lo que estoy haciendo ahora es:

s1 | xargs -d "," | xargs -n1 s2

¡Pero siento que lo estoy haciendo de una manera tan tonta! Entonces mi pregunta es:

¿Cuál es la forma correcta de hacerlo?

¡Mi problema con mi solución es que está llamando a xargs dos veces e iterando sobre la entrada dos veces, lo que no es razonable para mí, por supuesto, por medio del rendimiento! La respuesta xargs -d "," -n1parece agradable, pero no estoy seguro si solo está iterando una vez. Si es así, verifíquelo en su respuesta y lo aceptaré. Por cierto, prefiero no usar Perl ya que todavía está iterando dos veces y también puede que Perl no exista en muchos sistemas.

yukashima huksay
fuente
1
Si está funcionando, ¿por qué llamarlo tonto? Si el tiempo de ejecución es importante, entonces ese es otro asunto, déjelo aquí
George Udosen,
2
Prueba estos1 | xargs -d "," -n1 s2
George Udosen
1
Sospecho que parte del problema es malinterpretar el impacto de la iteración. Iterar sobre los elementos en algo así como una matriz asociativa es malo debido al costo de recorrer esa estructura de datos, pero "iterar" en general no es inherentemente malo. Específicamente, leer datos línea por línea a medida que se presenta en STDIN, no es un gran problema de rendimiento. El problema de rendimiento aquí es más el costo de generar un nuevo proceso y configurar la tubería. Mientras no lo haga con frecuencia (como en un bucle), preocuparse por el rendimiento de xargs es probablemente un ejemplo de optimización prematura.
dannysauer

Respuestas:

18

Esto también debería funcionar igualmente:

s1 | xargs -d "," -n1 s2

Caso de prueba:

printf 1,2,3,4 | xargs -d ',' -n1 echo

Resultado:

1
2
3
4

Si s1genera esa lista seguida de un carácter de nueva línea, querrá eliminarla, de lo contrario la última llamada sería en 4\nlugar de 4:

s1 | tr -d '\n' | xargs -d , -n1 s2
George Udosen
fuente
6

Si s2puede aceptar múltiples argumentos, podría hacer:

(IFS=,; ./s2 $(./s1))

que anula temporalmente IFS para que sea una coma, todo en una subshell, para que s2vea la salida de la s1división por comas. El subshell es una forma abreviada de cambiar IFS sin guardar el valor anterior o restablecerlo.

Una versión anterior de esta respuesta era incorrecta, probablemente debido a una configuración de IFS sobrante, que corrompe los resultados. Gracias a ilkkachu por señalar mi error .

Para realizar un bucle manual sobre las salidas y proporcionarlas individualmente s2, aquí, demostrando el almacenamiento y restablecimiento de IFS:

oIFS="$IFS"
IFS=,
for output in $(./s1); do ./s2 "$output"; done
IFS="$oIFS"

o ejecutar los bits IFS en una subshell como antes:

(
IFS=,
for output in $(./s1); do ./s2 "$output"; done
)
Jeff Schaller
fuente
1
¿Estás seguro de eso primero? bash -c 'IFS=, printf "%s\n" $(echo 1,2,3)'imprime 1,2,3en mi sistema, es decir, no hay división.
ilkkachu
Volveré a probar en un momento, pero sospecho que hay un comportamiento diferente basado en programas internos frente a programas externos.
Jeff Schaller
lo mismo con /usr/bin/printfy/bin/echo
ilkkachu
1
@ilkkachu Tiene razón, la división de palabras se produce antes de que IFS se reasigne en este caso (IFS=,; printf "%s\n" $(echo 1,2,3)), por otro lado, debería funcionar.
Undercat aplaude a Mónica el
1

Prueba esto:

s1 | perl -pe 's/,/\n/g' | xargs -n1 s2
Gilles Quenot
fuente
( El espacio ...)
Peter Mortensen
3
¿Por qué no simplemente tr ',' '\n'? No es necesario invocar algo tan (relativamente) pesado como Perl y expresiones regulares.
David Foerster