Bash -c com parâmetros posicionais

15

Geralmente, $0em um script é definido como o nome do script ou como ele foi chamado (incluindo o caminho). No entanto, se eu usar bashcom a -copção, $0será definido como o primeiro dos argumentos passados ​​após a cadeia de comando:

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

De fato, parece que os parâmetros posicionais foram alterados, mas inclusive $0. No entanto, shiftna 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 cadeias de comando? Observe que estou procurando o motivo, a lógica, por trás da implementação de um comportamento tão estranho.


Pode-se especular que tal sequência de comandos não precisaria do $0parâmetro normalmente definido, portanto, para economia, também é usado para argumentos normais. No entanto, nesse caso, o comportamento de shifté estranho. Outra possibilidade é $0usada para definir o comportamento dos programas (a la bashchamado como shou vimchamado como vi), mas isso não pode ser, pois $0aqui é visto apenas na cadeia de comando e não pelos programas chamados dentro dela. Como não consigo pensar em outros usos $0, não sei como explicar isso.

muru
fonte
Como uma observação lateral, eu vi o uso de -para o $0argumento como um idioma, como em sh -c 'foo $1 $2' - a b. Desta forma, parece muito normal (uma vez que você descobriu o que esse -meio, que é)
Volker Siegel
Você também pode echo 'echo the other side of this pipe globs "$@"' | sh -s -- *, porém, infelizmente, $0geralmente não é um parâmetro configurável com a -sopção tream ... No entanto, ele pode ser usado da mesma maneira que xargsgeralmente é. E outros além.
mikeserv
@VolkerSiegel Teria sido mais normal --, então isso poderia ter tido a interpretação usual de 'a partir daqui começa os argumentos', vista em alguns outros programas. Por outro lado, isso poderia confundir aqueles que não estão acostumados -ca pensar que --realmente têm essa interpretação.
Muru

Respostas:

10

Isso oferece a oportunidade de definir / escolher $0ao usar um script embutido. Caso contrário, $0seria 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 todas as conchas costumavam fazer isso. A concha Bourne fez. O shell Korn (e Almquist) escolheu o primeiro parâmetro para ir $1. POSIX acabou indo para o caminho Bourne, então kshe ashderivados voltou a isso mais tarde (mais sobre isso em http://www.in-ulm.de/~mascheck/various/find/#shell ). Isso significava que, por um longo período de tempo sh(que, dependendo do sistema, era baseado no shell Bourne, Almquist ou Korn), você não sabia se o primeiro argumento foi inserido $0ou $1, portanto, para portabilidade, você precisava 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, o POSIX especificou o novo comportamento no qual o primeiro argumento entra $0, para que agora possamos fazer:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt
Stéphane Chazelas
fonte
Eu corri: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txtno Ubuntu 14.04, festa version 4.3.11(1)-release, e eu tenho: txt files are *.txt. Oo
muru
2
@ muru, que está correto (e você provavelmente não possui txtarquivos no diretório atual). Veja tambémbash -c 'echo "${1?}"' foo
Stéphane Chazelas 27/08/14
Ah sim. Esse é um exemplo praticamente útil.
muru 27/08/14
1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txté fantasticamente inventivo!
iruvar 27/08/14
Ou você usar uma solução alternativa completamente robusta (sugerido por Stéphane Chazelas em comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... haha ...
mikeserv
3

Esse comportamento é definido pelo POSIX :

sh -c command_name [argumento ...]

Leia comandos do operando command_string. Defina o valor do parâmetro especial 0 (consulte Parâmetros especiais ) a partir do valor do operando command_name e os parâmetros posicionais ($ 1, $ 2 e assim por diante) em sequência dos demais operandos do argumento.

Quanto ao motivo pelo qual você deseja esse comportamento: isso diminui a diferença entre um script e uma -cstring. Você pode converter diretamente entre os dois sem nenhuma mudança de comportamento. Outras áreas consideram estas idênticas.

Também está alinhado com o modo como os argumentos do programa funcionam em geral: isso acaba chamando uma das execfunções, na qual o primeiro argumento fornecido também é $0, e igualmente comum esse argumento é o mesmo que o executável que você está executando. Às vezes, porém, você quer um valor especial lá e simplesmente não há outra maneira de obtê-lo. Dado que o argumento existe, ele precisa ser mapeado para alguma coisa, e o usuário precisa ser capaz de definir o que é isso.

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

Michael Homer
fonte
Você pode dar um exemplo (espero que no mundo real) do segundo parágrafo?
muru 27/08/14
Cuidado com a analogia com argv[0]. $0é definido apenas como argv[0]passado para execve()quando é parecido shou bash. Para scripts, $0é definido como o caminho fornecido como argumento 1, 2 ... para o intérprete (e para scripts executados diretamente, que vem do argumento do primeiro caminho para execve ()).
Stéphane Chazelas