xargs: agrega cada argumento con un parámetro

12

Sé que, teniendo en cuenta l="a b c",

echo $l | xargs ls

rendimientos

ls a b c

¿Qué construcción produce

mycommand -f a -f b -f c
no-un-usuario
fuente

Respuestas:

13

Una forma de hacerlo:

echo "a b c" | xargs printf -- '-f %s\n' | xargs mycommand

Esto supone a, by cno contiene espacios en blanco, nuevas líneas, comillas o barras diagonales inversas. :)

Con GNU findutilpuedes manejar el caso general, pero es un poco más complicado:

echo -n "a|b|c" | tr \| \\0 | xargs -0 printf -- '-f\0%s\0' | xargs -0 mycommand

Puede sustituir el |separador con algún otro carácter, que no aparece en a, bo c.

Editar: como señala @MichaelMol , con una lista muy larga de argumentos existe el riesgo de desbordar la longitud máxima de argumentos a los que se puede pasar mycommand. Cuando eso sucede, el último xargsdividirá la lista y ejecutará otra copia de mycommand, y existe el riesgo de que deje un no terminado -f. Si te preocupa esa situación, puedes reemplazar el último xargs -0anterior por algo como esto:

... | xargs -x -0 mycommand

Esto no resolverá el problema, pero abortará la ejecución mycommandcuando la lista de argumentos sea demasiado larga.

Satō Katsura
fuente
1
Corre un riesgo bastante feo de exceder ARG_MAXy tener un -fparámetro separado de su par.
Michael Mol
@MichaelMol Ese es un buen punto, pero no creo que haya una manera significativa de manejar esa situación sin saber más mycommand. Siempre puedes agregar -xal último xargs.
Satō Katsura
Creo que la solución adecuada es probablemente no usarla xargsen absoluto, y solo usarla findsi puede usarse. Esta solución es peligrosa; al menos debe advertir el caso de falla en su respuesta.
Michael Mol
@MichaelMol Realmente no veo cómo findsería una mejor solución general, particularmente cuando los argumentos iniciales no son nombres de archivo. :)
Satō Katsura
No sabemos cuáles son los argumentos iniciales; solo vemos el ejemplo dado, no el escenario que inspiró la pregunta. La intuición sugiere que con un argumento llamado -f, y con una herramienta de ejemplo lsutilizada para ilustración, @ not-a-user está tratando con nombres de archivos. Y dado findofrece el -execargumento, que le permite construir una línea de comandos, está bien. (Siempre y cuando mycommandse permita ejecutar más de una vez. Si no es así, entonces tenemos otro problema con el uso de xargsaquí ...)
Michael Mol
5

Una mejor manera de abordarlo (IMO) sería:

  • en zsh:

    l=(a b c)
    mycommand -f$^l

    o usando la compresión de la matriz para que el argumento no se adjunte a la opción:

    l=(a b c) o=(-f)
    mycommand "${o:^^l}"

    De esa manera, todavía funciona si la lmatriz contiene elementos vacíos o elementos que contienen espacios o cualquier otro carácter problemático para xargs. Ejemplo:

    $ l=(a '' '"' 'x y' c) o=(-f)
    $ printf '<%s>\n' "${o:^^l}"
    <-f>
    <a>
    <-f>
    <>
    <-f>
    <">
    <-f>
    <x y>
    <-f>
    <c>
  • en rc:

    l=(a b c)
    mycommand -f$l
  • en fish:

    set l a b c
    mycommand -f$l

(AFAIK, rcy fishno tiene compresión de matriz)

Con conchas de estilo Bourne de estilo antiguo como bash, siempre puedes hacer (aún permitiendo cualquier carácter en los elementos de la $@matriz):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Stéphane Chazelas
fuente
1
Otra forma de hacerlo en bash es con una variable de matriz con nombre. for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK si esto es más rápido, pero agregar 2 elementos a una matriz parece que debería ser O (n), mientras que su setciclo probablemente copia y vuelve a analizar la lista de argumentos acumulados cada vez (O (n ^ 2)).
Peter Cordes