Trasformare un array in argomenti di un comando?


40

Ho una serie di "opzioni" di un comando.

my_array=(option1 option2 option3)

Voglio chiamare questo comando in uno script bash, usando i valori dell'array come opzioni. Quindi, command $(some magic here with my_array) "$1"diventa:

command -option1 -option2 -option3 "$1"

Come posso farlo? È possibile?


1
Quindi vuoi aggiungere -all'inizio di ogni parola in my_array?
Kevin,

@Kevin: Sì, ed eseguilo. Tutte le righe sono all'interno dello stesso script bash. Lo sto chiedendo perché queste stesse opzioni verranno utilizzate in molti punti all'interno dello script, quindi invece di copiarle tutte, ho pensato a un array.
Qualcuno usa ancora il tuo MS-DOS il

1
Potrebbe sembrare inutile ma se stai realizzando grandi script di shell forse dovresti esaminare in perl, questo tipo di cose è molto facile da fare lì.
alfa64,

Risposte:


43

Preferirei un bashmodo semplice :

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

Una ragione per questo sono gli spazi. Ad esempio se hai:

my_array=(option1 'option2 with space' option3)

Le sedsoluzioni basate lo trasformeranno in -option1 -option2 -with -space -option3(lunghezza 5), ​​ma l' bashespansione sopra lo trasformerà in -option1 -option2 with space -option3(lunghezza ancora 3). Raramente, ma a volte questo è importante, ad esempio:

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

Se capisco correttamente, questa sostituzione di variabili non può essere racchiusa in una funzione, né essere assegnata a una variabile, giusto? Deve andare direttamente nell'invocazione del comando.
Konrad Rudolph,

1
@KonradRudolph, è possibile assegnare ad altra variabile finchè lo fai come assegnazione array: somevar=("${my_array[@]/#/-}")(Nota la parentesi intorno al valore.) Poi, naturalmente, si deve continuare la manipolazione come matrice quando lo si utilizza: echo "${somevar[@]}". Per quanto riguarda le funzioni, sei troppo limitato dalle possibilità della lingua. Si può passare un array: somefunc "${my_array[@]/#/-}", poi dentro lo avrete in $@: function somefunc() { echo "$@"; }. Ma lo stesso $@conterrà anche gli altri parametri, se esiste, e nessun altro modo per restituire l'array modificato se non stdout.
Manatwork

Abbastanza strano, non funziona con zsh. La prima volta mi sono imbattuto in qualcosa che funziona in bash ma non in zsh.
Ciao Angelo

@ Ciao Angelo, sei sicuro di aver usato @come pedice di array? Gli ho dato un test con zsh 5.5.1 e l'unica differenza ho notato che zsh ha preceduto ogni elemento solo quando scritto come ${my_array[@]/#/-}, mentre bash lo ha fatto anche per il *pedice.
Manatwork

Oh, scusa, avrei potuto fregare qualcosa, davvero funziona per me!
Ciao Angelo

2

Non ho elaborato che era in un array e pensavo che gli spazi fossero separati in una stringa. Questa soluzione funzionerà con quella, ma dato che è un array, vai con manatwork's solution ( @{my_array[@]/#/-}).


Questo non è poi così male sede con una subshell. La facilità con cui regex è dipende da cosa puoi garantire sulle opzioni. Se le opzioni sono tutte una "parola" ( a-zA-Z0-9solo), \<sarà sufficiente un semplice limite di parola iniziale ( ):

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

Se le tue opzioni hanno altri personaggi (molto probabilmente -), avrai bisogno di qualcosa di un po 'più complesso:

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

^corrisponde all'inizio della riga, [ \t]corrisponde a uno spazio o una scheda, \|corrisponde a entrambi i lati ( ^o [ \t]), i \( \)gruppi (per \|) e memorizza il risultato, \<corrisponde all'inizio di una parola. \1inizia la sostituzione mantenendo la prima partita dal parens ( \(\)) e -ovviamente aggiunge il trattino di cui abbiamo bisogno.

Questi funzionano con gnu sed, se non funzionano con i tuoi, fammi sapere.

E se utilizzerai la stessa cosa più volte, potresti volerlo calcolare una volta e archiviarlo:

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

Questo non ha funzionato con Mac OS X sed, quindi ho dovuto usare gsed(ho installato usando homebrew). Un'altra cosa: invece di echo $my_array, avevo bisogno di fare echo ${my_array[@]}per ottenere tutti gli elementi da my_array: echo $my_arraymi stava dando solo il primo elemento. Ma grazie per la risposta e la spiegazione del regex, specialmente riguardo al "confine di parole", questo è estremamente utile. :)
Qualcuno usa ancora MS-DOS il

Vorrei uscire dallo script se il sistema sed non è compatibile con questa funzione di limitazione delle parole. Come lo farei? Stampare la versione sed e confrontarla? Da quale versione di sed è stata aggiunta questa funzionalità?
Qualcuno usa ancora MS-DOS il

1
@ SomebodystillusesyouMS-DOS Il mio primo pensiero è quello di verificare direttamente se funziona - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Kevin,

2
Questo si interromperà se uno qualsiasi degli elementi dell'array contiene caratteri speciali, molto chiaramente e inesorabilmente spazi bianchi.
Gilles 'SO- smetti di essere malvagio' il

1
@ SomebodystillusesyouMS-DOS Gilles ha ragione, dovresti cambiare il tuo accetto 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'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.