Convierta argumentos de línea de comando en una matriz en Bash

162

¿Cómo convierto argumentos de línea de comandos en una matriz de script bash?

Quiero tomar esto:

./something.sh arg1 arg2 arg3

y convertirlo a

myArray=( arg1 arg2 arg3 )

para poder usar myArray para usarlo más en el script.

Esta publicación SO anterior se acerca, pero no explica cómo crear una matriz: ¿Cómo analizo los argumentos de la línea de comandos en Bash?

Necesito convertir los argumentos en una matriz de script bash regular; Me doy cuenta de que podría usar otros lenguajes (Python, por ejemplo) pero necesito hacer esto en bash. ¿Supongo que estoy buscando una función de "agregar" o algo similar?

ACTUALIZACIÓN: También quería preguntar cómo verificar cero argumentos y asignar un valor de matriz predeterminado, y gracias a la respuesta a continuación, pude hacer que esto funcionara:

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

Respuestas:

208

En realidad, los argumentos de la línea de comandos ya son prácticamente como una matriz. Al menos, puede tratar la $@variable como una matriz. Dicho esto, puedes convertirlo en una matriz real como esta:

myArray=( "$@" )

Si solo desea escribir algunos argumentos e introducirlos en el $@valor, use set:

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

Comprender cómo usar la estructura de argumentos es particularmente útil en POSIX sh, que no tiene nada más que una matriz.

kojiro
fuente
2
¡Gracias! ¡Funciona genial! Estaba a punto de preguntar cómo verificar cero argumentos y asignar un valor de matriz predeterminado, ¡y $ # funciona perfectamente para eso!
Suman
1
setle permite establecer parámetros posicionales para el alcance. También le permite establecer opciones de shell. Puede hacerlo set foo, lo que significa que se $1expande a "foo", pero si sus parámetros comienzan con un guión setsupondrá que quiere establecer una opción de shell. El doble guión garantiza que todos los siguientes parámetros se interpreten como parámetros posicionales que se establecerán.
kojiro
12
Un problema: echo $@imprimirá todos los argumentos, pero echo $myArraysolo imprimirá el primer elemento. Para verlos a todos, use echo ${myArray[@]}.
z0r
44
@ z0r Si no pone comillas dobles alrededor de esas expansiones, bash las volverá a dividir y posiblemente perderá el significado.
kojiro
Correcto, la forma general de "aplastar" una matriz y usar cada elemento es "${myArray[@]}". Si desea recorrer la matriz, necesita las comillas para evitar dividir sus elementos individuales en IFS
BallpointBen
66

Quizás esto pueda ayudar:

myArray=("$@") 

También puede iterar sobre argumentos omitiendo 'in':

for arg; do
   echo "$arg"
done

será equivalente

for arg in "${myArray[@]}"; do
   echo "$arg"
done
Nahuel Fouilleul
fuente
3
Preguntas para novatos: ¿cómo sabe bash qué poner en el argcampo? ¿Es una variable predefinida? ${var}se expande al contenido de var. ${var[n]}se expande al contenido del elemento nde la matriz var. ¿Se está ${var[@]}expandiendo toda la matriz, es decir ${var[0]} ${var[1]} ... ${var[n]}( nsiendo el índice del último elemento)?
Christian
44
[for] without [in] pasará sobre la matriz de argumentos $ @ ($ 1, $ 2, etc.). Que se puede configurar también con el comando [set], por ejemplo set - arg1 arg2
Nahuel Fouilleul
17

En realidad, se puede acceder a la lista de parámetros con $1 $2 ...etc.,
que es exactamente equivalente a:

${!i}

Entonces, la lista de parámetros podría cambiarse con set,
y ${!i}es la forma correcta de acceder a ellos:

$ 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 su caso específico, esto podría usarse (sin la necesidad de matrices), para establecer la lista de argumentos cuando no se proporcionó ninguno:

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

lo que se traduce en esta expresión aún más simple:

[ "$#" == "0" ] && set -- defaultarg1 defaultarg2
Yaron
fuente
¿No debería ser el ejemplo de eco: echo "$#" "$i+1" "${!i}";para obtener la salida exactamente como se muestra?
Zael
6

Aquí hay otro uso:

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

Más fácil Sin embargo, puede operar directamente en $@ ;)

Aquí se explica cómo pasar una lista de argumentos directamente desde el indicador:

function echoarg { for stuff in "$@" ; do echo $stuff ; done ; } 
    echoarg Hey Ho Lets Go
    Hey
    Ho
    Lets
    Go
runlevel0
fuente
1
Aún más fácil, for stuff in "$@" ; do ...es lo mismo que for stuff ; do ...:)
kkm
1

Vista de lado a lado de cómo la matriz y $ @ son prácticamente iguales.

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

Salida:

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
fuente
1

Vale la pena destacar la importancia de las comillas dobles. Supongamos que un argumento contiene espacios en blanco.

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

Salida:

> ./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
fuente