Transformar uma matriz em argumentos de um comando?


40

Eu tenho uma matriz de "opções" de um comando.

my_array=(option1 option2 option3)

Eu quero chamar esse comando em um script bash, usando os valores da matriz como opções. Então, command $(some magic here with my_array) "$1"torna-se:

command -option1 -option2 -option3 "$1"

Como eu posso fazer isso? É possível?


11
Então você deseja adicionar -ao início de cada palavra my_array?
Kevin

@ Kevin: Sim, e execute-o. Todas as linhas estão dentro do mesmo script bash. Estou perguntando isso, porque essas mesmas opções serão usadas em muitos lugares dentro do script, então, em vez de copiar todas elas, pensei em uma matriz.
Alguém ainda usa o MS-DOS

11
Pode parecer inútil, mas se você estiver criando grandes scripts de shell, talvez deva procurar no perl, esse tipo de coisa é muito fácil de fazer.
Alfa32

Respostas:


43

Eu preferiria uma bashmaneira simples :

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

Uma razão para isso são os espaços. Por exemplo, se você tiver:

my_array=(option1 'option2 with space' option3)

As sedsoluções baseadas o transformarão em -option1 -option2 -with -space -option3(comprimento 5), mas a bashexpansão acima o transformará em -option1 -option2 with space -option3(comprimento ainda 3). Raramente, mas às vezes isso é importante, por exemplo:

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 bem entendi, essa substituição de variável não pode ser agrupada em uma função, nem ser atribuída a uma variável, certo? Ele precisa ir diretamente para a chamada de comando.
Konrad Rudolph

11
@KonradRudolph, você pode atribuí-lo a outra variável, desde que você fazê-lo como atribuição de matriz: somevar=("${my_array[@]/#/-}")(Nota do parêntese em torno do valor). Então é claro que você tem que manter a manuseá-lo como uma matriz quando usá-lo: echo "${somevar[@]}". Em relação às funções, você está muito limitado pelas possibilidades da linguagem. Você pode passar um array: somefunc "${my_array[@]/#/-}", em seguida, dentro de você vai tê-lo em $@: function somefunc() { echo "$@"; }. Mas o mesmo $@conterá os outros parâmetros também, se existir, e não há outra maneira de retornar a matriz modificada que stdout.
manatwork

Estranho o suficiente, não funciona com o zsh. A primeira vez que deparei com algo que funciona no bash, mas não no zsh.
Hi-Angel

@ Oi, Angel, você tem certeza de que usou @como subscrito de matriz? Eu testei com o zsh 5.5.1 e a única diferença que notei foi que o zsh prefixou cada item apenas quando escrito como ${my_array[@]/#/-}, enquanto o bash também o fez por *subscrito.
manatwork

Oh, desculpe, eu posso ter ferrado alguma coisa, de fato funciona para mim!
Hi-Angel

2

Não processei que estava em uma matriz e estava pensando em espaço em branco separado em uma string. Esta solução funcionará com isso, mas, como é uma matriz, siga a solução do manatwork ( @{my_array[@]/#/-}).


Isso não é tão ruim com sedum subshell. A facilidade de regex depende do que você pode garantir sobre as opções. Se as opções forem todas uma "palavra" ( a-zA-Z0-9apenas), um limite simples da palavra inicial ( \<) será suficiente:

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

Se suas opções tiverem outros caracteres (provavelmente -), você precisará de algo um pouco mais complexo:

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

^corresponde ao início da linha, [ \t]corresponde a um espaço ou tabulação, \|corresponde a um dos lados ( ^ou [ \t]), \( \)agrupa (para \|) e armazena o resultado, \<corresponde ao início de uma palavra. \1inicia a substituição mantendo a primeira partida entre parens ( \(\)) e, -claro, adiciona o traço que precisamos.

Estes funcionam com o gnu sed, se não funcionarem com o seu, me avise.

E se você usar a mesma coisa várias vezes, é possível calculá-lo apenas uma vez e armazená-lo:

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

Isso não funcionou com o Mac OS X sed, então eu precisava usar gsed(instalei usando homebrew). Outra coisa: em vez de echo $my_array, eu precisava fazer echo ${my_array[@]}para obter todos os itens my_array: echo $my_arrayestava me dando apenas o primeiro elemento. Mas obrigado pela resposta e pela explicação da regex, especialmente sobre "limite de palavras", isso é extremamente útil. :)
Alguém ainda usa o MS-DOS

Gostaria de sair do script se o sed do sistema não for compatível com esse recurso de limite de palavras. Como eu faria isso? Imprimindo a versão sed e comparando-a? De qual versão do sed esse recurso foi adicionado?
Alguém ainda usa o MS-DOS

11
@ SomeebodystillusesyouMS-DOS Meu primeiro pensamento é verificar diretamente se funciona - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Kevin

2
Isso será interrompido se algum dos elementos da matriz contiver caracteres especiais, mais obviamente e inexoravelmente em branco.
Gilles 'SO- stop be evil'

11
@Alguémestilo de youMS-DOS Gilles está certo, você deve mudar sua aceitação para 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'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.