Transformer un tableau en arguments d'une commande?


40

J'ai un tableau des "options" d'une commande.

my_array=(option1 option2 option3)

Je veux appeler cette commande dans un script bash, en utilisant les valeurs de array comme options. Alors, command $(some magic here with my_array) "$1"devient:

command -option1 -option2 -option3 "$1"

Comment puis-je le faire? C'est possible?


1
Donc, vous voulez ajouter -au début de chaque mot my_array?
Kevin

@ Kevin: Oui, et exécutez-le. Toutes les lignes sont dans le même script bash. Je pose cette question parce que ces mêmes options vont être utilisées à de nombreux endroits dans le script, alors au lieu de les copier toutes, j'ai pensé à un tableau.
Quelqu'un t'utilise encore MS-DOS le

1
Cela peut sembler inutile, mais si vous créez de gros scripts shell, vous devriez peut-être examiner Perl, ce genre de chose est très facile à faire là-bas.
alfa64

Réponses:


43

Je préférerais une bashmanière simple:

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

Une des raisons est les espaces. Par exemple si vous avez:

my_array=(option1 'option2 with space' option3)

Les sedsolutions basées le transformeront en -option1 -option2 -with -space -option3(longueur 5), mais l' bashexpansion ci-dessus le transformera en -option1 -option2 with space -option3(longueur encore 3). Rarement, mais parfois c'est important, par exemple:

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 je comprends bien, cette substitution de variable ne peut pas être encapsulée dans une fonction, ni être affectée à une variable, non? Cela doit aller directement dans l'invocation de commande.
Konrad Rudolph

1
@KonradRudolph, vous pouvez l' affecter à d' autres variables aussi longtemps que vous le faites comme affectation de tableau: somevar=("${my_array[@]/#/-}")(Notez la parenthèse autour de la valeur.) Ensuite , bien sûr , vous devez garder la manipuler sous forme de tableau lors de son utilisation: echo "${somevar[@]}". En ce qui concerne les fonctions, vous êtes trop limité par les possibilités de la langue. Vous pouvez passer un tableau: somefunc "${my_array[@]/#/-}", puis à l' intérieur , vous aurez dans $@: function somefunc() { echo "$@"; }. Mais same $@contiendra aussi les autres paramètres, s’il existe, et aucun autre moyen de retourner le tableau modifié que stdout.
manatwork le

Assez bizarre, ça ne marche pas avec zsh. La première fois que je suis tombé sur quelque chose qui fonctionne dans bash mais pas zsh.
Bonjour Angel

@ Hi-Angel, êtes-vous sûr d'avoir utilisé @comme indice de tableau? J'ai fait un test avec zsh 5.5.1 et à la seule différence que j'ai remarqué que zsh préfixait seulement chaque élément lorsqu'il était écrit en tant que ${my_array[@]/#/-}, tandis que bash le faisait également pour l' *indice.
manatwork

Oh, désolé, j'aurais peut-être foutu quelque chose, ça marche vraiment pour moi!
Bonjour Angel

2

Je ne pensais pas que c'était dans un tableau et je pensais séparer les espaces par une chaîne. Cette solution fonctionnera avec cela, mais étant donné qu'il s'agit d'un tableau, utilisez la solution manatwork ( @{my_array[@]/#/-}).


Ce n'est pas si mal avec sedet un sous-shell. La simplicité de l'expression rationnelle dépend de ce que vous pouvez garantir concernant les options. Si toutes les options sont un "mot" ( a-zA-Z0-9seulement), un simple début de mot frontière ( \<) suffira:

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

Si vos options ont d'autres caractères (probablement -), vous aurez besoin de quelque chose d'un peu plus complexe:

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

^correspond au début de la ligne, [ \t]correspond à un espace ou une tabulation, \|correspond à l'un ^ou l' autre côté ( ou [ \t]), \( \)groupes (pour le \|) et stocke le résultat, \<correspond au début d'un mot. \1commence le remplacement en gardant le premier match des parens ( \(\)), et -ajoute bien sûr le tiret dont nous avons besoin.

Ceux-ci fonctionnent avec gnu sed, s'ils ne fonctionnent pas avec le vôtre, faites le moi savoir.

Et si vous utilisez la même chose plusieurs fois, vous pouvez simplement le calculer une fois et le stocker:

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

Cela ne fonctionnait pas avec Mac OS X sed, je devais donc utiliser gsed(j'ai installé en utilisant homebrew). Une autre chose: au lieu de echo $my_array, je devais faire echo ${my_array[@]}pour obtenir tous les éléments de my_array: echo $my_arrayme donnait seulement le premier élément. Mais merci pour la réponse et l'explication de la regex, spécialement à propos de "limite de mot", c'est extrêmement utile. :)
Quelqu'un t'utilise encore MS-DOS le

J'aimerais quitter le script si le fichier sed du système n'est pas compatible avec cette fonction de limite de mot. Comment pourrais-je le faire? Imprimer la version sed et la comparer? À partir de quelle version de sed cette fonctionnalité a-t-elle été ajoutée?
Quelqu'un t'utilise encore MS-DOS le

1
@ SomebodystillusesyouMS-DOS Ma première pensée est de vérifier directement si cela fonctionne - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Kevin

2
Cela se brisera si l'un des éléments du tableau contient des caractères spéciaux, le plus évidemment et inexorablement les blancs.
Gilles 'SO- arrête d'être méchant'

1
@ SomebodystillusesyouMS-DOS Gilles a raison, vous devez modifier votre acceptation en tant que 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'
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.