Converter argumentos de linha de comando em uma matriz no Bash

160

Como converter argumentos da linha de comando em uma matriz de scripts bash?

Eu quero pegar isso:

./something.sh arg1 arg2 arg3

e converta-o para

myArray=( arg1 arg2 arg3 )

para que eu possa usar o myArray para uso posterior no script.

Este post anterior do SO chega perto, mas não aborda como criar uma matriz: Como analiso argumentos de linha de comando no Bash?

Eu preciso converter os argumentos em uma matriz de script bash regular; Sei que poderia usar outras linguagens (Python, por exemplo), mas preciso fazer isso no bash. Eu acho que estou procurando uma função "acrescentar" ou algo semelhante?

UPDATE: Eu também queria perguntar como verificar se há zero argumentos e atribuir um valor de matriz padrão e, graças à resposta abaixo, consegui fazer isso funcionar:

if [ "$#" -eq 0 ]; then
  myArray=( defaultarg1 defaultarg2 )
else
  myArray=( "$@" )
fi
Suman
fonte

Respostas:

206

Na verdade, seus argumentos de linha de comando já são praticamente como uma matriz. Pelo menos, você pode tratar a $@variável como uma matriz. Dito isto, você pode convertê-lo em uma matriz real como esta:

myArray=( "$@" )

Se você deseja apenas digitar alguns argumentos e alimentá-los com o $@valor, use set:

$ set -- apple banana "kiwi fruit"
$ echo "$#"
3
$ echo "$@"
apple banana kiwi fruit

Entender como usar a estrutura de argumentos é particularmente útil no POSIX sh, que não tem mais nada como uma matriz.

kojiro
fonte
2
Obrigado! Funciona bem! Estava prestes a perguntar como verificar zero argumentos e atribuir um valor de matriz padrão, e o $ # funciona perfeitamente para isso!
Suman
1
setpermite definir parâmetros posicionais para o escopo. Também permite definir opções de shell. Você pode fazer set foo, o que significa que se $1expande para "foo", mas se seus parâmetros começarem com um traço set, você assumirá que você deseja definir uma opção de shell. O traço duplo garante que todos os seguintes parâmetros sejam interpretados como parâmetros posicionais a serem configurados.
Kojiro #
11
Uma dica: echo $@imprimirá todos os argumentos, mas echo $myArraysomente o primeiro elemento. Para vê-los todos, use echo ${myArray[@]}.
Z0R
4
@ z0r Se você não colocar aspas duplas em torno dessas expansões, o bash as redistribuirá e possivelmente perderá o significado.
Kojiro
Certo, a maneira geral de "dividir" uma matriz e usar cada elemento é "${myArray[@]}". Se você quiser percorrer a matriz, você precisa as aspas para a divisão evitar seus elementos individuais no IFS
BallpointBen
66

Talvez isso possa ajudar:

myArray=("$@") 

Também é possível iterar sobre argumentos, omitindo 'in':

for arg; do
   echo "$arg"
done

será equivalente

for arg in "${myArray[@]}"; do
   echo "$arg"
done
Nahuel Fouilleul
fonte
3
Perguntas para iniciantes: Como o bash sabe o que colocar em argcampo - é uma variável predefinida? ${var}é expandido para o conteúdo de var. ${var[n]}é expandido para o conteúdo do elemento nda matriz var. Está ${var[@]}expandindo toda a matriz, ou seja ${var[0]} ${var[1]} ... ${var[n]}( nsendo o último índice do elemento)?
Christian
4
[for] sem [in] fará um loop sobre a matriz de argumentos $ @ ($ 1, $ 2, etc.). Que também pode ser definido com o comando [set], por exemplo, set - arg1 arg2
Nahuel Fouilleul
17

Na verdade, a lista de parâmetros pode ser acessada com $1 $2 ...etc.
Qual é exatamente equivalente a:

${!i}

Portanto, a lista de parâmetros pode ser alterada com set
e ${!i}é a maneira correta de acessá-los:

$ set -- aa bb cc dd 55 ff gg hh ii jjj kkk lll
$ for ((i=0;i<=$#;i++)); do echo "$#" "$i" "${!i}"; done
12 1 aa
12 2 bb
12 3 cc
12 4 dd
12 5 55
12 6 ff
12 7 gg
12 8 hh
12 9 ii
12 10 jjj
12 11 kkk
12 12 lll

Para o seu caso específico, isso pode ser usado (sem a necessidade de matrizes), para definir a lista de argumentos quando nenhum foi fornecido:

if [ "$#" -eq 0 ]; then
    set -- defaultarg1 defaultarg2
fi

que se traduz nessa expressão ainda mais simples:

[ "$#" == "0" ] && set -- defaultarg1 defaultarg2
Yaron
fonte
O exemplo do eco não deveria ser: echo "$#" "$i+1" "${!i}";obter a saída exatamente como mostrado?
Zael
6

Aqui está outro uso:

#!/bin/bash
array=( "$@" )
arraylength=${#array[@]}
for (( i=0; i<${arraylength}; i++ ));
do
   echo "${array[$i]}"
done
tuğrul altun
fonte
4

Mais fácil ainda, você pode operar diretamente em $@ ;)

Aqui está como passar uma lista de argumentos diretamente do prompt:

function echoarg { for stuff in "$@" ; do echo $stuff ; done ; } 
    echoarg Hey Ho Lets Go
    Hey
    Ho
    Lets
    Go
runlevel0
fonte
1
Ainda mais fácil, for stuff in "$@" ; do ...é o mesmo que for stuff ; do ...:)
kkm
1

Vista lado a lado de como a matriz e $ @ são praticamente os mesmos.

Código:

#!/bin/bash

echo "Dollar-1 : $1"
echo "Dollar-2 : $2"
echo "Dollar-3 : $3"
echo "Dollar-AT: $@"
echo ""

myArray=( "$@" )

echo "A Val 0: ${myArray[0]}"
echo "A Val 1: ${myArray[1]}"
echo "A Val 2: ${myArray[2]}"
echo "A All Values: ${myArray[@]}"

Entrada:

./bash-array-practice.sh 1 2 3 4

Resultado:

Dollar-1 : 1
Dollar-2 : 2
Dollar-3 : 3
Dollar-AT: 1 2 3 4

A Val 0: 1
A Val 1: 2
A Val 2: 3
A All Values: 1 2 3 4
Kirin
fonte
1

Vale ressaltar a importância das aspas duplas. Suponha que um argumento contenha espaço em branco.

Código:

#!/bin/bash
printf 'arguments:%s\n' "$@"
declare -a arrayGOOD=( "$@" )
declare -a arrayBAAD=(  $@  )

printf '\n%s:\n' arrayGOOD
declare -p arrayGOOD
arrayGOODlength=${#arrayGOOD[@]}
for (( i=1; i<${arrayGOODlength}+1; i++ ));
do
   echo "${arrayGOOD[$i-1]}"
done

printf '\n%s:\n' arrayBAAD
declare -p arrayBAAD
arrayBAADlength=${#arrayBAAD[@]}
for (( i=1; i<${arrayBAADlength}+1; i++ ));
do
   echo "${arrayBAAD[$i-1]}"
done

Resultado:

> ./bash-array-practice.sh 'The dog ate the "flea" -- and ' the mouse.
arguments:The dog ate the "flea" -- and 
arguments:the
arguments:mouse.

arrayGOOD:
declare -a arrayGOOD='([0]="The dog ate the \"flea\" -- and " [1]="the" [2]="mouse.")'
The dog ate the "flea" -- and 
the
mouse.

arrayBAAD:
declare -a arrayBAAD='([0]="The" [1]="dog" [2]="ate" [3]="the" [4]="\"flea\"" [5]="--" [6]="and" [7]="the" [8]="mouse.")'
The
dog
ate
the
"flea"
--
and
the
mouse.
> 
Jacob Wegelin
fonte