Processar todos os argumentos, exceto o primeiro (em um script bash)

444

Eu tenho um script simples em que o primeiro argumento é reservado para o nome do arquivo e todos os outros argumentos opcionais devem ser passados ​​para outras partes do script.

Usando o Google, encontrei este wiki , mas ele forneceu um exemplo literal:

echo "${@: -1}"

Não consigo fazer mais nada funcionar, como:

echo "${@:2}"

ou

echo "${@:2,1}"

Eu recebo "Substituição ruim" do terminal.

Qual é o problema e como posso processar tudo, exceto o primeiro argumento passado para um script bash?

teta
fonte
21
Para chamar alguém confuso, o shebang errado foi fornecido, causando o "{@:2}"não trabalho, e é por isso que a resposta correta corresponde acima.
Guvante 22/10/2015
3
Você acabou de usar o shell padrão, que é traço no Ubuntu e em muitos outros Linux. No traço "$ {@: -1}" é interpretado como: {parameter: -word} - Use Valores padrão e use word se o parâmetro não estiver definido ou for nulo. Portanto, no traço "$ {@: -1}" resulta exatamente o mesmo que "$ @". Para usar o bash, use a primeira linha a seguir no arquivo de script: #! / Bin / bash
luart

Respostas:

660

Usa isto:

echo "${@:2}"

A seguinte sintaxe:

echo "${*:2}"

funcionaria tão bem, mas não é recomendado, porque, como o @Gordon já explicou, esse uso *executa todos os argumentos juntos como um único argumento com espaços, enquanto @preserva as quebras entre eles (mesmo que alguns dos argumentos contenham espaços ) Não faz a diferença echo, mas é importante para muitos outros comandos.

Oliver Charlesworth
fonte
7
Acabei de perceber que meu shebang era ruim: #!/usr/bin/env shé por isso que tive problemas. Você exemplo funciona bem, mesmo que acima fornecido, depois que eu removi que shebang
teta
110
Use em "${@:2}"vez disso - use *executa todos os argumentos juntos como um único argumento com espaços, enquanto @preserva as quebras entre eles (mesmo que alguns dos argumentos contenham espaços). A diferença não é perceptível echo, mas é importante para muitas outras coisas.
Gordon Davisson
2
@GordonDavisson O ponto principal aqui é executar os argumentos juntos. Se estivéssemos passando nomes de arquivos, você estaria correto. echoperdoa o suficiente para concatená-los para você; outros comandos podem não ser tão agradáveis. Não use apenas um ou outro: aprenda a diferença entre *e @e quando usar cada um. Você deveria usá-los igualmente. Um bom exemplo de quando isso será um problema: se $3contiver uma quebra de linha ( \n), ela será substituída por um espaço, desde que você tenha uma $IFSvariável padrão .
Zenexer
1
@Zenexer: Pelo que entendi, a questão era como passar todos os argumentos, exceto o primeiro, para "para outra parte do script", usando echoapenas como exemplo - nesse caso, eles não devem ser executados juntos. Na minha experiência, as situações em que você deseja que elas funcionem juntas são raras (veja esta pergunta em um exemplo ) e "$@"quase sempre é o que você deseja. Além disso, o problema mencionado com uma quebra de linha ocorre apenas se $@(ou $*) não estiver entre aspas duplas.
Gordon Davisson
@GordonDavisson Hmm ... você está certo, agora que penso nisso; a quebra de linha não deve ser um problema. Eu devo ter pensado em outra coisa. No entanto, ainda tenho que discordar sobre executá-los juntos; Eu preciso usar $*bastante nos meus scripts.
Zenexer 10/10
180

Se você deseja uma solução que também funcione na /bin/shtentativa

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

shift [n]muda os parâmetros posicionais n vezes. A shiftdefine o valor de $1para o valor de $2, o valor de $2para o valor de $3, e assim por diante, diminuindo o valor de $#um.

Ben Jackson
fonte
25
+1: você provavelmente deve economizar US $ 1 em uma variável antes de desviá-la.
precisa saber é o seguinte
3
Surpreendentemente foo=shift, não faz o que eu esperava.
amigos estão dizendo sobre keith smiley
Eu sei que isto é antiga, mas tentarfoo=$(shift)
Kidwai raumaan
12
@raumaankidwai Isso não funciona por 2 razões: 1) shift(no shell) não tem saída. Apenas descarta $1e muda tudo para baixo. 2) $(...)inicia um subshell, que possui seus próprios argumentos locais. Ela muda os argumentos no subnível, que não afeta o pai
Ben Jackson
Obrigado :) Existe alguma maneira de fazer o que somecommand "$1" "${@:2}"faz com este método (ou seja, shift "inline")?
Anônimo
0

Deparei com isso procurando algo mais. Embora o post pareça bastante antigo, a solução mais fácil no bash é ilustrada abaixo (pelo menos o bash 4) usando set -- "${@:#}"onde # é o número inicial do elemento do array que queremos preservar adiante:

    #!/bin/bash

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

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

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

Basicamente, o set -- "${@:3}"just aparece os dois primeiros elementos da matriz como shift do perl e preserva todos os elementos restantes, incluindo o terceiro. Eu suspeito que há uma maneira de destacar os últimos elementos também.

Pavman
fonte