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

39

Na documentação do Perl, perlrun (1) sugere o lançamento de scripts Perl usando um shell biling / Perl:

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

O que significa ${1+"$@"} ? Eu tentei usar "$@" (usando Bash como / bin / sh), e parece funcionar também.

Editar

Duas respostas abaixo dizem que é suposto ser ${1:+"$@"} . Estou ciente da sintaxe ${parameter:+word} ("Use Alternate Value") documentada no bash (1). No entanto, não estou convencido, porque

  1. Tanto ${1+"$@"} como "$@" funcionam bem, mesmo quando não há parâmetros. Se eu criar o 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);
    

    Eu posso fazer os dois funcionarem 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 reprovada) não documentada para ${parameter:+word} ? Alguém poderia confirmar essa hipótese?

    
por 200_success 19.03.2013 / 21:08

3 respostas

52

Isso é para compatibilidade 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/sh na maioria dos Unices comerciais.

É o ancestral da maioria das shells parecidas com Bourne , como ksh , bash ou zsh .

Ele tinha alguns recursos estranhos, muitos dos quais foram corrigidos em ksh e os outros shells e a nova especificação padrão de sh , um dos quais é este:

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

${var+something} se expande para "algo", a menos que $var seja desfeito. Está claramente documentado em todos os shells, mas é difícil encontrá-lo na documentação do bash , já que você precisa prestar atenção a esta frase:

When not performing substring expansion, using the forms documented below, bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset.

Portanto, ${1+"$@"} expande para "$@" apenas se $1 estiver definido ( $# > 0 ), o que funciona em torno dessa limitação do shell Bourne.

Observe que o shell Bourne é o único shell com esse problema. O sh s moderno (que é sh em conformidade com a especificação POSIX de sh (que o shell Bourne não é)) não tem esse problema. Portanto, você só precisa disso se precisar que seu código funcione em sistemas muito antigos, em que /bin/sh pode ser um shell Bourne em vez de um shell padrão (note que POSIX não especifica o local do padrão sh , portanto, por exemplo no Solaris antes do Solaris 11, /bin/sh ainda era um shell Bourne (embora não tivesse esse problema específico), enquanto o normal / padrão sh estava em outro local ( /usr/xpg4/bin/sh )).

Há um problema em que a página perlrun perldoc em que $0 não está citada.

Consulte o link para obter mais informações.

    
por 19.03.2013 / 22:06
3

Existe 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, "$ @" equivale à mesma coisa: "" . Mas usar ${1:+"$@"} seria "" para o primeiro e nenhum argumento passado para o segundo, que era a intenção.

Isso se torna importante se você estiver fazendo algo no 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 apenas retorna), não seria um shell interativo.

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

Iniciaria um shell interativo no host remoto porque o exec interpretaria corretamente a variável como nada, não como uma string vazia.

Leia sobre o uso de "$ {parameter: + word}" ("Usar Valor Alternativo") e sequências de expansão de variáveis semelhantes nas páginas de manual do bash.

    
por 19.03.2013 / 21:35
0

Resumi a resposta de Stéphane Chazelas:

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

então, se usar o segundo com o parâmetro "", significa que $ 1 é nulo, mas não testa se é nulo ou não, apenas vê que já foi configurado, mas vazio ou não, então será expanda $ @, mas você usar $ {1: + "$ @"} 'com "", não expandirá mais $@ .

    
por 11.03.2015 / 09:21

Tags