¿Transformar una matriz en argumentos de un comando?


40

Tengo una serie de "opciones" de un comando.

my_array=(option1 option2 option3)

Quiero llamar a este comando en un script bash, usando los valores de la matriz como opciones. Entonces, se command $(some magic here with my_array) "$1"convierte en:

command -option1 -option2 -option3 "$1"

¿Cómo puedo hacerlo? ¿Es posible?


1
¿Entonces quieres agregar -al comienzo de cada palabra my_array?
Kevin

@ Kevin: Sí, y ejecútalo. Todas las líneas están dentro del mismo script bash. Pregunto esto porque estas mismas opciones se usarán en muchos lugares dentro del script, por lo que en lugar de copiarlas todas, pensé en una matriz.
Alguien todavía te usa MS-DOS

1
Puede parecer inútil, pero si está creando scripts de shell grandes, tal vez debería buscar en Perl, este tipo de cosas es muy fácil de hacer allí.
alfa64

Respuestas:


43

Preferiría una bashforma simple :

command "${my_array[@]/#/-}" "$1"

Una razón para esto son los espacios. Por ejemplo si tienes:

my_array=(option1 'option2 with space' option3)

Las sedsoluciones basadas lo transformarán en -option1 -option2 -with -space -option3(longitud 5), pero la bashexpansión anterior lo transformará en -option1 -option2 with space -option3(longitud todavía 3). Raramente, pero a veces esto es importante, por ejemplo:

bash-4.2$ my_array=('Ffoo bar' 'vOFS=fiz baz')
bash-4.2$ echo 'one foo bar two foo bar three foo bar four' | awk "${my_array[@]/#/-}" '{print$2,$3}'
 two fiz baz three

Si entiendo correctamente, esta sustitución de variable no se puede incluir en una función, ni asignarse a una variable, ¿verdad? Necesita ir directamente a la invocación del comando.
Konrad Rudolph

1
@KonradRudolph, se puede asignar a otra variable, siempre y cuando lo haces como la asignación de matrices: somevar=("${my_array[@]/#/-}")(Nota del paréntesis alrededor del valor.) Entonces, por supuesto, usted tiene que mantener su manejo como un array cuando se la usa: echo "${somevar[@]}". En cuanto a las funciones, allí estás demasiado limitado por las posibilidades del lenguaje. Puede pasar un array: somefunc "${my_array[@]/#/-}", a continuación, en el interior se tienen en $@: function somefunc() { echo "$@"; }. Pero lo mismo $@también contendrá los otros parámetros, si existen, y no hay otra forma de devolver la matriz modificada que stdout.
manatwork

Por extraño que parezca, no funciona con zsh. La primera vez me topé con algo que funciona en bash pero no en zsh.
Hola Ángel

@ Hola, Ángel, ¿estás seguro de que utilizaste @como subíndice de matriz? Lo probé con zsh 5.5.1 y la única diferencia es que noté que zsh solo tenía el prefijo de cada elemento cuando se escribía como ${my_array[@]/#/-}, mientras que bash también lo hacía para el *subíndice.
manatwork

¡Oh, lo siento, podría haber jodido algo, de hecho funciona para mí!
Hola Ángel

2

No procesé que estaba en una matriz y estaba pensando en espacios en blanco separados en una cadena. Esta solución funcionará con eso, pero dado que es una matriz, vaya con la solución de manatwork ( @{my_array[@]/#/-}).


Esto no es tan malo con seduna subshell. Lo fácil que es la expresión regular depende de lo que pueda garantizar sobre las opciones. Si las opciones son todas una "palabra" ( a-zA-Z0-9solo), \<bastará con un simple límite de palabra inicial ( ):

command $(echo $my_array | sed 's/\</-/g') "$1"

Si sus opciones tienen otros caracteres (muy probablemente -), necesitará algo un poco más complejo:

command $(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g') "$1"

^coincide con el comienzo de la línea, [ \t]coincide con un espacio o tabulación, \|coincide con cualquier lado ( ^o [ \t]), \( \)agrupa (para \|) y almacena el resultado, \<coincide con el inicio de una palabra. \1comienza el reemplazo manteniendo la primera coincidencia de los parens ( \(\)) y, -por supuesto, agrega el guión que necesitamos.

Estos funcionan con gnu sed, si no funcionan con los suyos, hágamelo saber.

Y si va a usar lo mismo varias veces, es posible que desee calcularlo una vez y almacenarlo:

opts="$(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g')"
...
command $opts "$1"
command $opts "$2"

Esto no funcionaba con Mac OS X sed, así que necesitaba usarlo gsed(lo instalé usando homebrew). Otra cosa: en lugar de echo $my_array, tenía que hacer echo ${my_array[@]}para obtener todos los elementos my_array: echo $my_arrayme estaba dando solo el primer elemento. Pero gracias por la respuesta y la explicación de la expresión regular, especialmente sobre el "límite de palabras", esto es extremadamente útil. :)
Alguien todavía te usa MS-DOS el

Me gustaría salir de la secuencia de comandos si el sistema sed no es compatible con esta función de límite de palabras. ¿Cómo lo haría? ¿Imprime la versión sed y la compara? ¿Desde qué versión de sed se agregó esta característica?
Alguien todavía te usa MS-DOS el

1
@ SomebodystillusesyouMS-DOS Mi primer pensamiento es verificar directamente si funciona - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Kevin

2
Esto se romperá si alguno de los elementos de la matriz contiene caracteres especiales, el espacio en blanco más evidente e inexorable.
Gilles 'SO- deja de ser malvado'

1
@ SomebodystillusesyouMS-DOS Gilles tiene razón, debe cambiar su aceptación a manatwork.
Kevin

0
[srikanth@myhost ~]$ sh sample.sh 

-option1 -option2 -option3

[srikanth@myhost ~]$ cat sample.sh

#!/bin/bash

my_array=(option1 option2 option3)

echo ${my_array[@]} | sed 's/\</-/g'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.