Geralmente usamos $@
para representar todos os argumentos, exceto $0. No entanto, não sei o que $@
é estrutura de dados.
Por que se comporta de maneira diferente $*
ao incluir aspas duplas, alguém poderia me dar uma explicação no nível do intérprete?
Ele pode ser iterado em loop for, então parece ser array. No entanto, também pode ecoar inteiramente com simple echo $@
, se for uma matriz, apenas o primeiro elemento será mostrado. Devido à limitação do shell, não posso escrever mais código de experimento para realizá-lo.
Diferença entre este post : Este post mostra como $@
se comporta de forma diferente do $*
. Mas eu estou querendo saber sobre o tipo de dados de $@
. Shell como linguagem de interpretação, como Python, deve representar dados de acordo com uma série de tipos fundamentais. Ou em outras palavras, quero saber como $@ é armazenado na memória do computador.
É uma string, uma string de várias linhas ou uma matriz?
Se for um tipo de dados exclusivo, é possível definir uma variável personalizada como uma instância desse tipo?
Isso começou como um hack no shell Bourne. No shell Bourne, a divisão de palavras IFS foi feita (após a tokenização) em todas as palavras no contexto da lista (argumentos de linha de comando ou as palavras
for
em que os loops fazem loop). Se você tinha:Essa segunda linha seria tokenizada em 3 palavras,
$var
seria expandida e split+glob seria feito em todas as três palavras, então você acabaria executandoed
comt
,f
,le.txt
,f
,le2.txt
como argumentos.Citar partes disso impediria o split+glob. O shell Bourne inicialmente lembrava quais caracteres eram citados definindo o 8º bit neles internamente (isso mudou mais tarde quando o Unix se tornou 8 bits limpo, mas o shell ainda fazia algo semelhante para lembrar qual byte foi citado).
Ambos
$*
e$@
foram a concatenação dos parâmetros posicionais com espaço entre eles. Mas houve um processamento especial de$@
quando entre aspas duplas. Se$1
contidofoo bar
e$2
contidobaz
,"$@"
se expandiria para:(com o
^
s acima indicando qual dos caracteres tem o 8º bit definido). Onde o primeiro espaço foi citado (tinha o 8º bit definido), mas não o segundo (o adicionado entre as palavras).E é a divisão IFS que se encarrega de separar os argumentos (supondo que o caractere de espaço esteja
$IFS
como está por padrão). Isso é semelhante a 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
"$@"
se expandiria para a string vazia em vez de nada quando a lista de parâmetros posicionais estava vazia (você teve que contornar isso com${1+"$@"}
), por que ele não manteve os parâmetros posicionais vazios e por que"$@"
não 'não funciona quando$IFS
não contém o caractere de espaço.A intenção era poder passar a lista de argumentos literalmente para outro comando, mas isso não funcionou corretamente para a lista vazia, para elementos vazios ou quando
$IFS
não continha espaço (os dois primeiros problemas acabaram sendo corrigidos em versões posteriores ).O shell Korn (no qual a especificação POSIX é baseada) mudou esse comportamento de algumas maneiras:
edit
oufile.txt
no exemplo acima)$*
e$@
são unidos com o primeiro caractere de$IFS
ou espaço quando$IFS
está vazio, exceto que para um quote"$@"
, esse joiner é sem aspas como no Bourne shell e para um quote"$*"
quandoIFS
está vazio, os parâmetros posicionais são acrescentados sem separador.${array[@]}
${array[*]}
uma reminiscência de Bourne$*
e$@
mas começando no índice 0 em vez de 1, e esparso (mais como arrays associativos), o que significa que$@
não pode realmente ser tratado como um array ksh (compare comcsh
/rc
/zsh
/fish
/yash
onde$argv
/$*
são normais matrizes)."$@"
when$#
is 0 agora se expande para nada em vez da string vazia,"$@"
funciona quando$IFS
não contém espaços, exceto quandoIFS
está vazio. Um sem aspas$*
sem curingas se expande para um argumento (onde os parâmetros posicionais são unidos com espaço) quando$IFS
está vazio.ksh93 corrigiu os poucos problemas restantes acima. Em ksh93,
$*
e$@
se expande para a lista de parâmetros posicionais, separados independentemente do valor de$IFS
, e então split+globbed+brace-expanded em contextos de lista,$*
unidos com o primeiro byte (não caractere) de$IFS
,"$@"
em contextos de lista expande para a lista de parâmetros posicionais, independentemente do valor de$IFS
. No contexto de não lista, como emvar=$@
,$@
é unido com espaço, independentemente do valor de$IFS
.bash
As matrizes de ksh são projetadas após as matrizes ksh. As diferenças são:$IFS
em vez de para byte$*
quando não entre aspas em contexto não listado quando$IFS
está vazio.Embora a especificação POSIX costumava ser bastante vaga, agora ela especifica mais ou menos o comportamento do bash.
É diferente das matrizes normais em
ksh
oubash
em que:"${@:0}"
que inclui$0
(não é um parâmetro posicional, e em funções lhe dá o nome da função ou não dependendo do shell e de como a função foi definida)).shift
pode ser usado.Em
zsh
ouyash
onde os arrays são arrays normais (não esparsos, os índices começam em um como em todos os outros shells, exceto ksh/bash),$*
é tratado como um array normal.zsh
tem$argv
como alias para ele (para compatibilidade comcsh
).$*
é o mesmo que$argv
ou${argv[*]}
(argumentos unidos com o primeiro caractere de$IFS
mas ainda separados em contextos de lista)."$@"
gosta"${argv[@]}"
ou"${*[@]}"}
sofre o processamento especial no estilo Korn.É um parâmetro especial que se expande para os valores dos parâmetros posicionais... Mas isso é um detalhe sobre a terminologia.
Podemos visualizar os parâmetros posicionais como partes de
$@
, portanto, possui vários elementos distintos ($1
,$2
...), que podem ser acessados independentemente e são nomeados por números naturais consecutivos. Isso o torna algo que geralmente é chamado de array.A sintaxe é um pouco estranha, porém, e até limitada. Não há como modificar um único elemento da matriz individualmente. Em vez disso, tudo deve ser definido de uma só vez. (Você pode usar
set -- "$@" foo
para acrescentar um valor ouset -- "${@:1:2}" foo "${@:3}"
adicionar um valor no meio. Mas em ambos os casos você precisa escrever toda a lista resultante.)Porque eles são definidos para se comportar de maneira diferente.
Se você quer dizer o fato de que
a=(foo bar asdf); echo $a
produzirá apenasfoo
, então isso é principalmente uma peculiaridade da sintaxe do shell e o fato de que os arrays nomeados no estilo ksh foram criados depois dos parâmetros posicionais e$@
. Plain$a
é o mesmo${a[0]}
que tem o significado compatível com versões anteriores de um único valor escalar, independentemente dea
ser uma matriz ou uma variável escalar simples.O
@
sinal referente à lista inteira foi reutilizado com arrays nomeados, pois"${a[@]}"
é a maneira de obter a lista inteira. Comparado com arrays nomeados, com$@
, as chaves e colchetes desnecessários e o nome são simplesmente ignorados.Isso depende da implementação, você terá que procurar o código-fonte de qualquer shell específico que você se importe.
Uma matriz, principalmente. Embora diferente dos arrays nomeados no estilo ksh, já que eles podem ter inteiros não negativos arbitrários como índices, não apenas consecutivos como com
$@
. (Ou seja, um array nomeado pode ser esparso e ter, por exemplo, os índices1
,3
e4
, com0
e2
ausente. Isso não é possível com os parâmetros posicionais.)Não é uma única string, pois pode ser expandida para elementos distintos, e chamar os elementos de linhas também não é correto, pois qualquer variável regular, ou um dos parâmetros posicionais (elementos de
$@
) também pode conter novas linhas.Não. Mas arrays nomeados são provavelmente mais úteis de qualquer maneira.