Por que $ 0 não é um parâmetro posicional?

4

Eu li que os parâmetros posicionais começam em $1 (por exemplo: $1 , $2 , $3 e assim por diante são parâmetros posicionais). Mas $0 não é um parâmetro posicional.

Mas por que $0 não é um parâmetro posicional?

Acho que isso pode ser um motivo, mas não tenho certeza:

Os parâmetros posicionais só recebem seus valores quando um script é executado. Por exemplo: se fizermos ./myScript Hello , então $1 terá o valor Hello . Mas $0 pode ter seu valor em duas ocasiões: quando um script é executado (ele teria o valor do nome do script) e quando bash é executado sem um script (ele teria o valor bash ou -bash ).

    
por Steve 23.12.2017 / 19:35

3 respostas

13

@kkachu explicou melhor do que eu poderia. Vou apenas adicionar uma nota de histórico.

O shell que veio com as primeiras versões do Unix (mais tarde chamado de shell Thompson) não tinha nenhuma variável, mas você já podia escrever scripts simples que usassem parâmetros.

     sh [ name [ arg1 ... [ arg9 ] ] ]

The name is the name of a file which will be read and in‐
terpreted.   If  not given, this subinstance of the shell
will continue to read the standard input file.

In command lines in the  file  (not  in  command  input),
character  sequences of the form "$n", where n is a digit
0, ..., 9, are replaced by the nth argument to the  invo‐
cation of the shell (argn).  "$0" is replaced by name.

$1 ... $n já eram os argumentos e $0 o nome do script (não o 1º argumento), mas não foram chamados parâmetros posicionais .

Observe que, naquela época, $1 foi literalmente substituído pelo primeiro argumento antes da interpretação do shell.

Por exemplo, um script que tinha:

echo $1

chamado como

sh script 'foo; echo bar'

executaria echo foo; echo bar . Esse shell era muito simples, escrito para computadores com poucas centenas de kilobytes de memória.

A shell Bourne chegou quase uma década depois (no final dos anos 70) com uma versão do Unix que introduziu o ambiente e outras coisas boas.

O shell Bourne veio com variáveis e muito mais construções de programação.

A terminologia parâmetro posicional , pelo menos quando se trata de shells Unix, foi introduzida pelo shell Bourne, e referenciada à mesma coisa, $1 ... $n para os argumentos do script (e $0 ainda é o nome do script). Como no shell Thompson, você poderia se referir apenas aos primeiros 9 argumentos ( $1 to $9 ) com parâmetros posicionais (mas use shift ou "$@" ou for i do loops para acessar o restante) (e isso também explica (portabilidade para trás) porque você precisa de ${10} em vez de $10 nas implementações mais modernas de sh para as 10 primeiras).

Desta vez, sh script 'foo; echo bar' não causou mais que echo bar seja executado, mas mesmo assim o Bourne shell introduziu o infame split + glob, eu suponho não quebrar muito compatível com o shell Thompson, então script 'foo *' script tem echo $1 ainda chamou echo com foo e a lista de arquivos no diretório atual como argumentos (como no shell Thompson, mas desta vez através de um mecanismo diferente).

scripts também foram chamados de procedimentos de shell (note que o shell Bourne ainda não tem funções):

2.0 Shell procedures

The shell may be used to read and execute commands contained
in a file.  For example,

         sh file [ args  ]

calls the shell to read commands from file.  Such a file  is
called  a  command  procedure or shell procedure.  Arguments
may be supplied with the call and are referred  to  in  file
using  the  positional parameters $1, $2...

As funções foram introduzidas pela primeira vez no shell Korn (baseado no shell Bourne) no início dos anos 80 com um

function foo {
  ...
}

sintaxe.

Funções foram eventualmente adicionadas ao shell Bourne, mais tarde, em SysVR2 (1984), com uma sintaxe diferente:

foo() any-command

(mas com comportamento inesperado quando any-command era um comando simples e tinha redirecionamentos que provavelmente é a razão pela qual o POSIX requer apenas comandos compostos (como { ...; } o mais usado) ser reconhecido lá para o POSIX sh ).

No shell Korn e Bourne, $0 na função ainda era o nome do script, não o nome da função (enquanto os parâmetros $1 , $2 posicional se referem aos argumentos da função).

Isso mudou em ksh93 para o estilo function f { da definição da função, onde $0 se torna o nome da função na função.

Em zsh , $0 é o nome da função, como em ksh93. zsh também introduziu funções anônimas com o seguinte:

function { echo $1, $2; } foo bar

ou

(){ echo $1, $2; } foo bar

sintaxe, em que $0 se torna (anon) (ou fica com o nome do script com set +o functionargzero , como quando em sh / ksh emulation).

Em zsh , como em csh , os argumentos dos scripts também estão na matriz $argv , o que levanta a confusão sobre o nome do programa chamado de forma semelhante aos argumentos.

Lá, você pode atribuir valores aos parâmetros de posição com:

argv[1]=value

ou

1=value

(e também 0=newprogramname para alterar o nome do programa).

Em outros shells semelhantes a Bourne, você precisa usar set para atribuir todos de uma só vez:

set -- arg1 arg2

E você não pode alterar $0 .

Em rc (pelo menos a porta Unix), você não pode fazer:

1 = value

Mas você pode fazer:

* = (arg1 arg2)

para definir os parâmetros posicionais. Você não pode alterar $0 em rc , mas você pode em seu derivado es com 0=newprogramname como em zsh .

TL; DR

O $1 , $2 ... para se referir aos argumentos dos scripts vem do shell Thompson, mas ainda não foram chamados parâmetros posicionais . E $0 (provavelmente escolhido em referência a argv[0] como @ikkachu bem colocado) refere-se ao nome do script.

O termo parâmetros posicionais vem do shell Bourne.

$0 não é um parâmetro posicional porque não se refere a um argumento do script. Refere-se ao nome do script (ou ao shell argv[0] quando o shell não executa nenhum script).

    
por 23.12.2017 / 23:17
9

Há um claro paralelo dos parâmetros numerados ( $0 , $1 , ...) para argv[] , a matriz que contém os parâmetros da linha de comando quando um processo é iniciado. O primeiro elemento da matriz, argv[0] , geralmente contém o nome do processo e os argumentos reais começam em argv[1] .

(Normalmente, não é necessário. A descrição de execve(2) states : "O valor em argv[0] deve apontar para uma string de nome de arquivo associada ao processo que está sendo iniciado")

Pelo menos de fato, é fácil imaginar que a convenção foi copiada diretamente para o shell.

Os valores não são copiados diretamente, no entanto. Pelo menos nos meus sistemas, o processo do shell que começa ao executar ./script.sh com o hashbang #!/bin/bash -x obtém os parâmetros /bin/bash , -x , ./script.sh . Ou seja, o valor que vai para $0 , conforme visto pelo script, está em argv[2] do processo do shell.

Eu suponho que a maioria das pessoas consideraria o nome do comando distinto de seus parâmetros, então $0 é funcionalmente diferente dos outros, então não é razoável chamá-lo de forma diferente também.

Claro que poderíamos ter uma linguagem de script que tivesse uma convenção de nomenclatura diferente. Perl coloca o nome do programa em uma variável chamada $0 , e os argumentos na matriz @ARGV , iniciando no índice zero, ou seja, $ARGV[0] etc.

De qualquer forma, a resposta mais óbvia seria dizer que $0 não é um parâmetro posicional, porque o padrão diz :

Em 2.5 Parâmetros e variáveis

2.5.1 Positional Parameters

A positional parameter is a parameter denoted by the decimal value represented by one or more digits, other than the single digit 0.

2.5.2 Special Parameters

# Expands to the decimal number of positional parameters. The command name (parameter 0) shall not be counted in the number given by '#' because it is a special parameter, not a positional parameter.

0 (Zero.) Expands to the name of the shell or shell script.

    
por 23.12.2017 / 22:05
4

$ 0 na verdade é um parâmetro posicional (também indicado nos comentários de Michael Homer abaixo, POSIX diz "... dando os nomes dos argumentos como parâmetros posicionais numerados de 1 a n, e o nome do comando (ou no caso de uma função dentro de um script, o nome do script) como o parâmetro posicional numerado 0 ".

É o parâmetro de, como se poderia esperar, a posição 0, que representa o nome do comando invocador.

Isso é muito útil, pois permite que o mesmo script se comporte de maneira diferente, dependendo de seu nome invocado, permitindo que o mesmo executável tenha múltiplas funções (pense em links simbólicos, por exemplo). Isso permite que o processo saiba qual é o nome que o invoca.

Se você der uma olhada rápida em seu / bin, você verá muitos exemplos disso como bzcat, bzcmp, bzip2, ...; mdir, mcat, mcd, mdel, ...; xz, xzcat, unxz e muitos outros. A maneira como o processo sabe qual função executar é verificando o parâmetro posicional 0.

Nota: Algumas páginas man do shells podem lidar com a terminologia de maneira diferente: a página man do bash diz que não é um parâmetro posicional, a página man do ksh (e a definição POSIX) dizem que é. Independentemente de qualquer discussão de terminologia, em ambos (bash e ksh), $ 0 contém o nome do comando ou script de chamada. Caso seja um shell interativo, $ 0 conterá o nome do shell (bash, ksh, ...), como é o nome do processo.

    
por 23.12.2017 / 19:42