Bash -c com parâmetros posicionais

14

Geralmente, $0 em um script é definido como o nome do script ou o que foi invocado como (incluindo o caminho). No entanto, se eu usar bash com a opção -c , $0 será definido como o primeiro dos argumentos passados após a sequência de comandos:

bash -c 'echo $0 ' foo bar 
# foo 

Na verdade, parece que os parâmetros posicionais foram alterados, mas incluindo $0 . No entanto shift na cadeia de comando não afeta $0 (como normal):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

Por que esse comportamento aparentemente estranho para strings de comando? Note que estou procurando a razão, a lógica, por trás da implementação desse comportamento estranho.

Poder-se-ia especular que tal string de comando não precisaria do parâmetro $0 como usualmente definido, então, para economia, ele também é usado para argumentos normais. No entanto, nesse caso, o comportamento de shift é ímpar. Outra possibilidade é que $0 seja usado para definir o comportamento dos programas (a la bash chamado como sh ou vim chamado como vi ), mas isso não pode ser, pois $0 aqui é visto apenas na cadeia de comando e não por programas chamados dentro dela. Não consigo pensar em nenhum outro uso para $0 , por isso não sei explicar isso.

    
por muru 27.08.2014 / 12:11

2 respostas

9

Isso lhe dá a oportunidade de definir / escolher $0 ao usar um script in-line. Caso contrário, $0 seria apenas bash .

Então você pode fazer por exemplo:

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

Nem todos os shells costumavam fazer isso. O shell Bourne fez. O shell Korn (e Almquist) escolheu o primeiro parâmetro para $1 . POSIX acabou optando pelo Bourne, então ksh e ash derivativos voltaram a isso mais tarde (mais sobre isso em link ). Isso significava que por muito tempo para sh (que dependendo do sistema era baseado no shell Bourne, Almquist ou Korn), você não sabia se o primeiro argumento entrava em $0 ou $1 , então para portabilidade, você tinha que fazer coisas como:

sh -c 'echo foo in "$1"' foo foo

Ou:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

Felizmente, POSIX especificou o novo comportamento em que o primeiro argumento entra em $0 , então agora podemos fazer portably:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt
    
por 27.08.2014 / 12:40
3

Esse comportamento é definido pelo POSIX :

sh -c command_name [argument...]

Read commands from the command_string operand. Set the value of special parameter 0 (see Special Parameters) from the value of the command_name operand and the positional parameters ($1, $2, and so on) in sequence from the remaining argument operands.

Quanto a por que você deseja esse comportamento: isso suaviza a lacuna entre um script e uma string -c . Você pode converter diretamente entre os dois sem qualquer mudança de comportamento. Outras áreas confiam que estas são idênticas.

Também está de acordo com o funcionamento geral dos argumentos do programa: em última análise, isso se resume a chamar um dos exec funciona, em que o primeiro argumento fornecido também é $0 , e igualmente comumente esse argumento é o mesmo que o executável que você está executando. Às vezes, porém, você quer um valor especial lá, e não haveria outra maneira de obtê-lo. Dado que o argumento existe, ele tem que mapear para algo, e o usuário precisa ser capaz de definir o que é isso.

Esta consistência (e provável acidente histórico) leva à situação que você encontra.

    
por 27.08.2014 / 12:22

Tags