O que $ {1 + "$ @"} significa em um script de shell e como ele difere de "$ @"?

43

Na documentação do Perl, o perlrun (1) sugere o lançamento de scripts Perl usando um cabeçalho bilíngue do shell / Perl:

#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
    if 0;

O que ${1+"$@"}significa isso ? Tentei usar "$@"(em vez disso, usando Bash como / bin / sh), e parece funcionar da mesma forma.


Editar

Duas respostas abaixo dizem que deveria ser ${1:+"$@"}. Estou ciente da ${parameter:+word}sintaxe ("Usar valor alternativo") documentada no bash (1). No entanto, não estou convencido, porque

  1. Ambos ${1+"$@"}e "$@"funcionam muito bem, mesmo quando não há parâmetros. Se eu criar simple.sh como

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 "$@"'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    e question.sh como

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 ${1+"$@"}'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    Posso fazer com que ambos trabalhem de forma idêntica:

    $ ./question.sh 
    $VAR1 = [];
    $ ./question.sh a
    $VAR1 = [
              'a'
            ];
    $ ./question.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./question.sh ""
    $VAR1 = [
              ''
            ];
    $ ./simple.sh 
    $VAR1 = [];
    $ ./simple.sh a
    $VAR1 = [
              'a'
            ];
    $ ./simple.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./simple.sh ""
    $VAR1 = [
              ''
            ];
    
  2. Outras fontes na Internet também usam ${1+"$@"}, incluindo um hacker que parece saber o que está fazendo.

Talvez ${parameter+word}seja uma sintaxe alternativa (ou obsoleta) indocumentada para ${parameter:+word}? Alguém poderia confirmar essa hipótese?

200_success
fonte
Parece grawlixes
Martin Thoma

Respostas:

53

Isso é compatível com o shell Bourne. O shell Bourne era um shell antigo que foi lançado pela primeira vez com a versão 7 do Unix em 1979 e ainda era comum até meados dos anos 90, como /bin/shna maioria dos Unices comerciais.

É o ancestral da maioria das conchas tipo Bourneksh , como , bashou zsh.

Tinha algumas características estranhas, muitas das quais foram fixadas kshe as outras conchas e a nova especificação padrão de sh, uma das quais é esta:

Com o shell Bourne (pelo menos as variantes em que não foi corrigido): "$@"expande para um argumento vazio se a lista de parâmetros posicionais estiver vazia ( $# == 0) em vez de nenhum argumento.

${var+something}expande para "alguma coisa", a menos que $varesteja desativado. Está claramente documentado em todas as conchas, mas é difícil de encontrar na bashdocumentação, pois você precisa prestar atenção a esta frase:

Quando não estiver executando a expansão de substring, usando os formulários documentados abaixo, o bash testa um parâmetro não definido ou nulo. A omissão dos dois pontos resulta em um teste apenas para um parâmetro não definido .

Portanto, ${1+"$@"}expande "$@"apenas se $1for definido ( $# > 0), que contorna essa limitação do shell Bourne.

Observe que o shell Bourne é o único shell com esse problema. Os shs modernos (que estão em shconformidade com a especificação POSIX de sh(que o shell Bourne não é)) não têm esse problema. Portanto, você só precisa disso se precisar que seu código funcione em sistemas muito antigos, onde /bin/shpode ser um shell Bourne em vez de um shell padrão (observe que o POSIX não especifica a localização do padrão sh, por exemplo, no Solaris antes do Solaris 11, /bin/shainda era um shell Bourne (embora não tivesse esse problema específico) enquanto o normal / padrão shestava em outro local ( /usr/xpg4/bin/sh)).

Há um problema nessa perlrunpágina perldoc que $0ainda não é citado.

Veja http://www.in-ulm.de/~mascheck/various/bourne_args/ para mais informações.

Stéphane Chazelas
fonte
Não é possível reproduzir na herança. Provavelmente não aplicável ao Solaris.
ormaaj
2
@ormaaj. Sim, veja a página que eu vinculei. Foi corrigido no SVR3 e no Solaris / SunOS acima de 5 rebased no SVR4. E essa página menciona que o 4.1.x também não tinha o problema.
Stéphane Chazelas 19/03/2013
Ah entendo. <3 páginas de verificação.
ormaaj
3

Há uma diferença entre:

command ""

e

command

Em um, você está passando um argumento que é uma string vazia. No segundo, não há argumentos passados.

Para ambos, "$ @" vai equiparar à mesma coisa: "". Mas usar ${1:+"$@"}seria ""para o primeiro e nenhum argumento passaria para o segundo, que era a intenção.

Isso se torna importante se você estiver executando algo do script abaixo, chamado sshwrapper, que você chama com um comando opcional ou sem argumentos para obter um shell interativo.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" "$@"

Isso tentaria executar "" no host remoto (que retorna), não seria um shell interativo.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" ${1:+"$@"}

Iniciava um shell interativo no host remoto porque o exec interpretaria corretamente a variável como nada, não como uma sequência vazia.

Leia sobre o uso de "$ {parameter: + word}" ("Use Alternate Value") e cadeias de expansão variáveis ​​semelhantes nas páginas de manual do bash.

Arcege
fonte
isso parece se aplicar apenas ao shell Bourne e não a qualquer shimplementação compatível com POSIX (consulte outra resposta).
törzsmókus 7/15/15
4
Não, ${1:+"$@"}seria expandido para nenhum argumento se $1estivesse vazio. Você quer ${1+"$@"}. Basicamente, você o inverteu.
Stéphane Chazelas
0

Resumi a resposta de Stéphane Chazelas:

  • $ {1: + "$ @"} 'test se $ 1 nulo ou não definido
  • $ {1 + "$ @"} 'teste se $ 1 não definido

portanto, se usar o segundo com o parâmetro "", isso significa que $ 1 é nulo, mas não testa se é nulo ou não, basta ver que já foi definido, no entanto, está vazio ou não e, portanto, expandirá $ @ , mas você usa $ {1: + "$ @"} 'com "", ele não será mais expandido $@.

Kevin
fonte
7
Isso certamente resumidos, embora possa não esclarecer ...
Archemar