Considere o seguinte exemplo:
IFS=:
x="a :b" # three spaces
echo ["$x"] # no word splitting
# [a :b] # as is
echo [$x] # word splitting
# [a b] # four spaces
A divisão de palavras identifica as palavras "a "
(três espaços) e "b"
, separadas por dois pontos, echo
junta as palavras com um espaço no meio.
No entanto, ao usar o valor de $x
como um argumento de função, acho difícil interpretar os resultados.
args(){ echo ["$*"];}
args a :b # three spaces
# [a::b]
e:
args(){ echo [$*];}
args a :b # three spaces
# [a b] # two spaces
$*
expande para o valor de todos os parâmetros posicionais combinados. Além disso, "$*"
é equivalente a "$1c$2"
, onde c
é o primeiro caractere do valor da variável IFS.
args(){ echo ["$1"]["$2"]; }
args a :b # three spaces
# [a][:b]
e:
args(){ echo [$1][$2]; }
args a :b # three spaces
# [a][ b]
A divisão de palavras sempre deve ocorrer quando houver expansões sem aspas. Aqui "$1"
e $1
são iguais e em ambos os casos não usam o :
delimitador. [$2]
-> [ b]
também não está claro.
Provavelmente, antes de aplicar a divisão do IFS, outras regras de tokenização são usadas, mas não consegui encontrá-las.
A divisão de palavras aplica-se apenas a expansões sem aspas (expansão de parâmetro, expansão aritmética e substituição de comando) em shells modernos do tipo Bourne (no
zsh
, apenas substituição de comando, a menos que você use um modo de emulação).Quando você faz:
A divisão de palavras não está envolvida.
É a análise do shell que os tokeniza, descobre que a primeira não é uma de suas palavras-chave e, portanto, é um comando simples com 3 argumentos:
args
,a
e:b
. A quantidade de espaço não fará nenhuma diferença lá. Observe que não são apenas espaços, também tabulações e, em alguns shells (comoyash
oubash
) qualquer caractere considerado em branco em sua localidade (embora no caso debash
, não os multibyte)¹.Mesmo no shell Bourne, onde a divisão de palavras também se aplicava a argumentos não citados de comandos, independentemente de serem o resultado de expansões ou não, isso seria feito no topo (muito depois) da tokenização e da análise de sintaxe.
Na casca Bourne, em
Isso não analisaria isso como:
Mas primeiro como um
while
com um comando simples e aedit
palavra (já que é um argumento, mas não abid=did
palavra que é uma atribuição) desse comando simples seria dividido emed
et
para que oed
comando com os 3 argumentosed
e fosse executado comot
ofoo
condição dessewhile
loop.A divisão de palavras não faz parte da análise de sintaxe. É como um operador que é aplicado implicitamente a argumentos (também em
for
palavras de loop, arrays e com algum shell o alvo de redirecionamentos e alguns outros contextos ) para as partes deles que não são citadas. O que é confuso é que isso é feito implicitamente . Você não fazcmd split($x)
, você fazcmd $x
e osplit()
( na verdadeglob(split())
) está implícito. Emzsh
, você deve solicitá-lo explicitamente para expansões de parâmetros (split($x)
está$=x
lá ($=
parecendo uma tesoura)).Então, agora, para seus exemplos:
a
e:b
argumentos deargs
join com o primeiro caractere$IFS
que dáa::b
(observe que é uma má ideia usar[...]
aqui, pois é um operador globbing).$*
(que contéma::b
) é dividido ema
, a string vazia eb
. Então é:nenhuma surpresa, pois não há divisão de palavras.
Isso é como:
as
$2
(:b
) seria dividido na string vazia eb
.Um caso em que você verá variações entre as implementações é quando
$IFS
estiver vazio.Dentro:
Em algumas conchas (a maioria hoje em dia), você vê
E nem
<ab>
mesmo se"$*"
expandiria paraab
. Esses shells ainda separam essesa
e osb
parâmetros de posição e isso agora se tornou um requisito POSIX na versão mais recente do padrão.Se você fez:
você veria
<ab>
como as informações quea
eb
eram 2 argumentos separados foram perdidas quando atribuídas a$var
.¹, claro, não são apenas os espaços em branco que delimitam as palavras. Tokens especiais na sintaxe do shell também funcionam, cuja lista depende do contexto. Na maioria dos contextos,
|
,||
,&
,;
, nova linha,<
,>
,>>
... delimitam palavras. Porksh93
exemplo, você pode escrever um comando sem espaço em branco como: