Procese todos los argumentos excepto el primero (en un script bash)

444

Tengo un script simple donde el primer argumento está reservado para el nombre del archivo, y todos los demás argumentos opcionales deben pasarse a otras partes del script.

Usando Google encontré este wiki , pero proporcionó un ejemplo literal:

echo "${@: -1}"

No puedo hacer que nada más funcione, como:

echo "${@:2}"

o

echo "${@:2,1}"

Recibo "Mala sustitución" de la terminal.

¿Cuál es el problema y cómo puedo procesar todos menos el primer argumento pasado a un script bash?

theta
fuente
21
Para llamar a alguien más confundido, se proporcionó el shebang incorrecto que hace "{@:2}"que no funcione, por lo que la respuesta correcta coincide con la anterior.
Guvante
3
Acaba de usar el shell predeterminado, que es dash en Ubuntu y muchos otros Linux. En el guión, "$ {@: -1}" se interpreta como: {parámetro: -word}: use valores predeterminados y use word si el parámetro no está definido o es nulo. Entonces, en el guión "$ {@: -1}" resulta exactamente igual que "$ @". Para usar bash solo use la siguiente primera línea en el archivo de script: #! / Bin / bash
luart

Respuestas:

660

Utilizar este:

echo "${@:2}"

La siguiente sintaxis:

echo "${*:2}"

funcionaría bien, pero no se recomienda, porque como ya explicó @Gordon , que al usarlo *, ejecuta todos los argumentos juntos como un solo argumento con espacios, mientras @conserva los descansos entre ellos (incluso si algunos de los argumentos contienen espacios) ) No hace la diferencia con echo, pero es importante para muchos otros comandos.

Oliver Charlesworth
fuente
77
Me acabo de dar cuenta de que mi shebang era malo: #!/usr/bin/env shpor eso tuve problemas. Su ejemplo funciona bien, igual que el anterior, después de que eliminé ese shebang
theta
110
Use en su "${@:2}"lugar: el uso *ejecuta todos los argumentos juntos como un solo argumento con espacios, mientras @conserva los descansos entre ellos (incluso si algunos de los argumentos contienen espacios). La diferencia no se nota con echo, pero es importante para muchas otras cosas.
Gordon Davisson
2
@GordonDavisson El punto principal aquí es ejecutar los argumentos juntos. Si pasáramos nombres de archivo, estaría en lo correcto. echoes perdonar lo suficiente como para concatenarlos por ti; otros comandos podrían no ser tan agradables. No solo use uno u otro: aprenda la diferencia entre *y @, y cuándo usar cada uno. Deberías estar usándolos casi por igual. Un buen ejemplo de cuándo esto será un problema: si $3contiene un salto de línea ( \n), se reemplazará con un espacio, siempre que tenga una $IFSvariable predeterminada .
Zenexer
1
@Zenexer: Según tengo entendido, la pregunta era cómo pasar todos menos el primer argumento de "a otra parte del guión", con echosolo utiliza como un ejemplo - en cuyo caso se deben no pueden ejecutar juntos. En mi experiencia, las situaciones en las que desea que se ejecuten juntas son raras (consulte esta pregunta para ver un ejemplo ) y "$@"casi siempre es lo que desea. Además, el problema que menciona con un salto de línea solo ocurre si $@(o $*) no está entre comillas dobles.
Gordon Davisson
@GordonDavisson Hmm ... tienes razón, ahora que lo pienso; el salto de línea no debería ser un problema. Debo haber estado pensando en otra cosa. Sin embargo, todavía tengo que estar en desacuerdo sobre ejecutarlos juntos; Necesito usar con $*bastante frecuencia en mis scripts.
Zenexer
180

Si quieres una solución que también funcione en /bin/shprobar

first_arg="$1"
shift
echo First argument: "$first_arg"
echo Remaining arguments: "$@"

shift [n]desplaza los parámetros posicionales n veces. A shiftestablece el valor de $1al valor de $2, el valor de $2al valor de $3, y así sucesivamente, disminuyendo el valor de $#uno.

Ben Jackson
fuente
25
+1: Probablemente debería guardar $ 1 en una variable antes de cambiarlo.
Glenn Jackman
3
Sorprendentemente foo=shiftno hace lo que esperaba.
Keith Smiley
Sé que esto es viejo, pero intentefoo=$(shift)
raumaan kidwai
12
@raumaankidwai Eso no funciona por 2 razones: 1) shift(en shell) no tiene ningún resultado. Simplemente descarta $1y cambia todo hacia abajo. 2) $(...)inicia una subshell, que tiene sus propios argumentos locales. Cambia los argumentos en la subshell, lo que no afecta al padre
Ben Jackson
Gracias :) ¿Hay alguna forma de hacer lo que somecommand "$1" "${@:2}"hace con este método (es decir, cambiar "en línea")?
Anónimo
0

Encontré esto buscando algo más. Si bien la publicación parece bastante antigua, la solución más fácil en bash se ilustra a continuación (al menos bash 4) usando set -- "${@:#}"donde # es el número inicial del elemento de matriz que queremos conservar hacia adelante:

    #!/bin/bash

    someVar="${1}"
    someOtherVar="${2}"
    set -- "${@:3}"
    input=${@}

    [[ "${input[*],,}" == *"someword"* ]] && someNewVar="trigger"

    echo -e "${someVar}\n${someOtherVar}\n${someNewVar}\n\n${@}"

Básicamente, el elemento set -- "${@:3}"emerge de los dos primeros elementos de la matriz, como el desplazamiento de Perl, y conserva todos los elementos restantes, incluido el tercero. Sospecho que también hay una manera de resaltar los últimos elementos.

Pavman
fuente