¿Cómo uso $ * mientras omito ciertas variables de entrada como $ 1 y $ 2 en el script bash?

15

Por ejemplo,

elif [[ $append = $1 ]]
then
  touch ~/directory/"$2".txt
  echo "$variable_in_question" >> ~/directory/"$2".txt

para crear un archivo de texto que contenga todas las entradas siguientes "$2", o agregar un archivo de texto existente con todas las entradas siguientes "$2", ¿qué usaría en lugar de la "$variable_in_question"línea 4?

Básicamente quiero "$*", pero omitiendo "$1"y "$2".

Oreoplasma
fuente

Respuestas:

32

Puede usar la bashexpansión de parámetros para especificar un rango, esto también funciona con parámetros posicionales. Para $3... $nsería:

"${@:3}" # expands to "$3" "$4" "$5" …
"${*:3}" # expands to "$3 $4 $5 …"

Tenga en cuenta que ambos $@e $*ignore el primer argumento $0. Si se pregunta cuál usar en su caso: es muy probable que desee una cotización $@. No lo use a $*menos que explícitamente no quiera que los argumentos se citen individualmente.

Puedes probarlo de la siguiente manera:

$ bash -c 'echo "${@:3}"' 0 1 2 3 4 5 6
3 4 5 6
$ echo 'echo "${@:3}"' >script_file
$ bash script_file 0 1 2 3 4 5 6
2 3 4 5 6

Tenga en cuenta que en el primer ejemplo $0se llena con el primer argumento, 0mientras que cuando se usa en un script $0se llena con el nombre del script, como muestra el segundo ejemplo. El nombre del script, por bashsupuesto, es el primer argumento, solo que normalmente no se percibe como tal; lo mismo se aplica a un script ejecutable y llamado "directamente". Entonces, en el primer ejemplo tenemos $0= 0, $1= 1etc. mientras que en el segundo es $0= script_file, $1= 0, $2= 1etc .; ${@:3}selecciona cada argumento que comienza con $3.

Algunos ejemplos adicionales para posibles rangos:

 # two arguments starting with the third
$ bash -c 'echo "${@:3:2}"' 0 1 2 3 4 5 6
3 4
 # every argument starting with the second to last one
 # a negative value needs either a preceding space or parentheses
$ bash -c 'echo "${@: -2}"' 0 1 2 3 4 5 6
5 6
 # two arguments starting with the fifth to last one
$ bash -c 'echo "${@:(-5):2}"' 0 1 2 3 4 5 6
2 3

Otras lecturas:

postre
fuente
27

Puedes usar el shiftincorporado:

$ help shift
shift: shift [n]
    Shift positional parameters.

    Rename the positional parameters $N+1,$N+2 ... to $1,$2 ...  If N is
    not given, it is assumed to be 1.

    Exit Status:
    Returns success unless N is negative or greater than $#.

Ex. dado

$ cat argtest.bash 
#!/bin/bash

shift 2

echo "$*"

luego

$ ./argtest.bash foo bar baz bam boo
baz bam boo
conductor de acero
fuente
3
@Oreoplasma Creo que vale la pena mencionar que el shiftenfoque hará que sea imposible acceder $1y $2después de que los haya cambiado. En el script que usa $2junto con $variable_in_question, debe cambiar eso o usar el enfoque de Expansión de parámetros.
postre
66
shiftes bueno cuando el primero o más argumentos son especiales y que tiene sentido para recogerlos a cabo en variables independientes ( foo="$1"; bar="$2";o if [[ something with $1 ]];then blah blah; shift. Pero @ respuesta de postre con el método no destructivo es agradable en otros casos cuando se hace todavía desea la lista completa más adelante , o cuando usa una sintaxis más elegante para seleccionar un rango limitado de args, no hasta el infinito, sin introducir args vacíos a un comando si $@no tiene tantos elementos.
Peter Cordes
13

En general, puede copiar los parámetros posicionales en una matriz, eliminar los índices arbitrarios de la matriz y luego usar la matriz para expandir exactamente los índices que desee, sin perder sus argumentos originales.

Por ejemplo, si quisiera todos los argumentos excepto el primero, cuarto y quinto:

args=( "$@" )
unset args[0] args[3] args[4]
echo "${args[@]}"

En la copia, los índices se desplazan por 1, ya $0que no forma parte de $@.

muru
fuente
1
Por supuesto, es posible combinar eso, por ejemplo, echo "${args[@]:2}"para el tercer y último argumento.
postre