Qual é a estrutura de dados de $ @ no shell?

13

Geralmente usamos $@para representar todo o argumento, 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 em nível de intérprete?

Ele pode ser iterado no loop for, portanto parece ser um array. No entanto, ele também pode ecoar totalmente com o simples echo $@, se for uma matriz, apenas o primeiro elemento será mostrado. Devido à limitação do shell, não posso escrever mais código do experimento para executá-lo.

Diferença entre este post : Este post mostra como $@se comporta de maneira diferente $*. Mas eu estou querendo saber sobre o tipo de dados de $@. O shell como uma 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 com 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?

davmos
fonte
1
Possível duplicata de Qual é a diferença entre $ * e $ @?
Haxiel 27/02/19
@Haxiel, acho que não, escrevi a diferença deles na parte inferior do meu post.
davmos 27/02/19
Você seria melhor atendido testando a diferença na saída com printf '%s\n' "$@"e printf '%s\n' "$*". O echoutilitário apenas exibe seus argumentos, não importa se eles são um ou muitos. Ambas são matrizes (de cadeias), mas se comportam de maneira diferente quando são citadas duas vezes. Se um deles fosse uma sequência de linhas múltiplas, eles não seriam capazes de armazenar sequências de linhas múltiplas (o que podem). Não está claro qual problema você está tentando resolver.
Kusalananda
2
Sua pergunta é o equivalente a perguntar o que é uma @varvariável no Perl, em termos de armazenamento subjacente. Do ponto de vista de um programa Perl comum, isso realmente não importa, além de ser acessível como uma matriz / lista (e pelo fato de haver contextos nos quais uma lista é esperada).
Kusalananda

Respostas:

16

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.

Stéphane Chazelas
fonte
8

No entanto, não sei o que $@é estrutura de dados .

É um parâmetro especial que se expande para os valores dos parâmetros posicionais ... Mas isso é bastante detalhado sobre a terminologia.

Podemos visualizar os parâmetros posicionais como partes de $@, para que ele tenha vários elementos distintos ( $1, $2...), que podem ser acessados ​​independentemente e são nomeados por números naturais consecutivos. Isso torna algo que geralmente é chamado de matriz.

A sintaxe é um pouco estranha 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 -- "$@" foopara acrescentar um valor ou set -- "${@:1:2}" foo "${@:3}"adicionar um valor no meio. Mas nos dois casos, você deve escrever a lista resultante inteira.)

Por que se comportar de maneira diferente $*ao incluir aspas duplas,

Porque eles são definidos para se comportar de maneira diferente.

No entanto, ele também pode ecoar totalmente com o simples echo $@, se for uma matriz, apenas o primeiro elemento será mostrado.

Se você quer dizer o fato de que a=(foo bar asdf); echo $aa saída será justa foo, isso é principalmente uma sintaxe da sintaxe do shell, e o fato de que as matrizes nomeadas no estilo ksh foram criadas depois dos parâmetros posicionais e $@. Simples $aé o mesmo ${a[0]}que tem o significado compatível com versões anteriores de um único valor escalar, independentemente de se aser uma matriz ou uma variável escalar simples.

O @sinal referente à lista inteira foi reutilizado com matrizes nomeadas, e "${a[@]}"é assim que se obtém a lista inteira. Comparado às matrizes nomeadas, com $@, os colchetes e colchetes desnecessários e o nome são ignorados.

Ou, em outras palavras, quero saber como $@armazenado na memória do computador.

Isso depende da implementação, você terá que procurar o código-fonte de qualquer shell em particular que lhe interesse.

É uma string, uma string com várias linhas ou uma matriz?

Uma matriz, principalmente. Embora sejam diferentes dos arrays nomeados no estilo ksh, pois eles podem ter números inteiros não negativos arbitrários como índices, e não apenas números consecutivos $@. (Ou seja, uma matriz chamada pode ser escassa, e têm por exemplo, os índices 1, 3e 4, com 0e 2faltando. Isso não é possível com os parâmetros de posição.)

Não é uma string única, pois pode ser expandida para elementos distintos, e chamar as linhas de elementos também não é correto, pois qualquer variável regular ou um dos parâmetros posicionais (elementos de $@) também pode conter novas linhas.

Se for um tipo de dados exclusivo, é possível definir uma variável personalizada como uma instância desse tipo?

Não. Mas matrizes nomeadas são provavelmente mais úteis de qualquer maneira.

ilkkachu
fonte
1
+1. TL: DR $@não é uma estrutura de dados, é uma das poucas funções / operadores para expandir a estrutura de dados de parâmetros posicionais.
Peter Cordes