Isso começou como um hack no shell Bourne. No shell Bourne, a divisão de palavras do IFS foi realizada (após a tokenização) em todas as palavras no contexto da lista (argumentos da linha de comando ou nas palavras em que os forloops se repetem). Se você tinha:
IFS=i var=file2.txt
edit file.txt $var
Essa segunda linha seria tokenised em 3 palavras, $varseria ampliado, e dividir + glob seria feito em todas as três palavras, então você iria acabar correndo edcom t, f, le.txt, f, le2.txtcomo argumentos.
Citar partes disso impediria a divisão + glob. O shell Bourne inicialmente lembrou quais caracteres foram citados, definindo o 8º bit neles internamente (que mudou mais tarde quando o Unix ficou 8 bits limpo, mas o shell ainda fazia algo semelhante para lembrar qual byte era mencionado).
Ambos $*e $@foram a concatenação dos parâmetros posicionais com o espaço intermediário. Mas havia um processamento especial de $@quando estava entre aspas duplas. Se $1continha foo bare $2continha baz, "$@"expandiria para:
foo bar baz
^^^^^^^ ^^^
(com os ^s acima indicando quais dos caracteres têm o oitavo bit definido). Onde o primeiro espaço foi citado (o oitavo bit foi definido), mas não o segundo (o que foi adicionado entre as palavras).
E é a divisão do IFS que cuida da separação dos argumentos (assumindo que o caractere de espaço esteja $IFScomo está por padrão). É semelhante ao modo como $*foi expandido em seu antecessor o shell Mashey (ele próprio baseado no shell Thomson, enquanto o shell Bourne foi escrito do zero).
Isso explica por que, no shell Bourne, inicialmente "$@"seria expandido para a cadeia vazia, em vez de nada quando a lista de parâmetros posicionais estava vazia (era necessário contornar isso ${1+"$@"}), por que não mantinha os parâmetros posicionais vazios e por que "$@"não funcionou quando $IFSnão continha o caractere de espaço.
A intenção era poder passar a lista de argumentos literalmente para outro comando, mas isso não funcionava corretamente para a lista vazia, para elementos vazios ou quando $IFSnão continham espaço (os dois primeiros problemas foram corrigidos em versões posteriores )
O shell Korn (no qual a especificação POSIX se baseia) mudou esse comportamento de algumas maneiras:
- A divisão do IFS é feita apenas no resultado de expansões não citadas (não em palavras literais como
editou file.txtno exemplo acima)
$*e $@são unidos ao primeiro caractere de $IFSou espaço quando $IFSestá vazio, exceto que para um citado "$@", esse marceneiro não é citado como no shell Bourne e, para um citado "$*"quando IFSestá vazio, os parâmetros posicionais são anexados sem separador.
- adicionou suporte para matrizes,
${array[@]} ${array[*]}lembrando as de Bourne $*e $@começando no índice 0 em vez de 1, e esparsas (mais parecidas com matrizes associativas), o que significa que $@realmente não podem ser tratadas como uma matriz ksh (compare com csh/ rc/ zsh/ fish/ / yashwhere $argv/ $*são normais matrizes).
- Os elementos vazios são preservados.
"$@"Quando $#0 é agora expandido para nada em vez da cadeia vazia, "$@"funciona quando $IFSnão contém espaços, exceto quando IFSestá vazio. Um $*sem aspas sem curingas se expande para um argumento (onde os parâmetros posicionais são unidos com espaço) quando $IFSestá vazio.
O ksh93 corrigiu os problemas restantes acima. Em ksh93, $*e se $@expande para a lista de parâmetros posicionais, separados independentemente do valor de $IFS, e depois divididos + globbed + brace-expandido em contextos de lista, $*unidos ao primeiro byte (não caractere) de $IFS, "$@"em contextos de lista se expandem para a lista de parâmetros posicionais, independentemente do valor de $IFS. No contexto de não lista, como em var=$@, $@é associado o espaço, independentemente do valor de $IFS.
bashAs matrizes são projetadas após as ksh. As diferenças são:
- sem chave de expansão após expansão não cotada
- primeiro caractere de em
$IFSvez de para byte
- algumas diferenças de maiúsculas e minúsculas, como a expansão de
$*quando não citado no contexto de não lista quando $IFSestá vazio.
Enquanto a especificação POSIX costumava ser bastante vaga, agora mais ou menos especifica o comportamento do bash.
É diferente de matrizes normais kshou bashem que:
- Os índices começam em 1 em vez de 0 (exceto no
"${@:0}"que inclui $0(não é um parâmetro posicional, e em funções fornece o nome da função ou não, dependendo do shell e como a função foi definida)).
- Você não pode atribuir elementos individualmente
- não é escasso, você não pode desmarcar elementos individualmente
shift pode ser usado.
Em zshou yashonde matrizes são matrizes normais (não esparsas, os índices começam em um como em todos os outros shells, exceto ksh / bash), $*é tratado como uma matriz normal. zshtem $argvcomo um apelido para ele (para compatibilidade com csh). $*é o mesmo que $argvou ${argv[*]}(argumentos associados ao primeiro caractere de, $IFSmas ainda separados em contextos de lista). "$@"gosta "${argv[@]}"ou "${*[@]}"}passa pelo processamento especial no estilo Korn.