Divisão de palavras em parâmetros posicionais

7

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 pelos dois pontos, em seguida, echo une 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" , em que 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 há expansões sem aspas. Aqui "$1" e $1 são iguais e, em ambos os casos, eles não usam o delimitador : . [$2] - > [ b] também não está claro.

Provavelmente, antes de aplicar o IFS-splitting, outras regras de tokenização são usadas, mas não consegui encontrá-las.

    
por antonio 03.12.2017 / 23:23

1 resposta

12

A divisão de palavras se aplica apenas a expansões sem aspas (expansão de parâmetros, expansão aritmética e substituição de comandos) em shells modernos semelhantes a Bourne (em zsh , somente substituição de comando, a menos que você use um modo de emulação).

Quando você faz:

args a    :b

A divisão de palavras não está envolvida de todo.

É a análise de shell que simboliza esses, descobre que o primeiro não é uma de suas palavras-chave e, portanto, é um comando simples com três argumentos: args , a e :b . A quantidade de espaço não fará diferença lá. Observe que não são apenas espaços, também guias e, em alguns shells (como yash ou bash ), qualquer caractere considerado em branco em sua localidade (embora no caso de bash , 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 ou não o resultado de expansões, isso seria feito no topo (long after). análise de sintaxe.

No shell Bourne, em

IFS=i
while bib=did edit foo

Isso não seria analisar como:

"wh" "le b" "b=d" "d ed" "t foo"

Mas primeiro como while com um comando simples e a edit word (como é um argumento, mas não a bid=did word que é uma atribuição) desse comando simples seria mais dividido em ed e t para que o comando ed com os 3 argumentos ed , t e foo seja executado como a condição desse loop while .

A divisão de palavras não é parte da análise de sintaxe. É como um operador que é aplicado implicitamente a argumentos (também em for loop words, 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 é feito implicitamente . Você não usa cmd split($x) , você cmd $x e split() ( realmente glob(split()) ) estão implícitos. Em zsh , você precisa solicitá-lo explicitamente para expansões de parâmetros ( split($x) is $=x there ( $= parecendo um par de tesouras)).

Agora, para seus exemplos:

args(){ echo ["$*"];}
args a   :b  # three spaces
# [a::b]
Os argumentos

a e :b de args se juntaram ao primeiro caractere de $IFS , o que dá a::b (note que é uma má idéia usar [...] aqui, pois é um operador globbing). / p>

args(){ echo [$*];}
args a   :b  # three spaces
# [a  b]     # two spaces

$* (que contém a::b ) é dividido em a , a string vazia e b . Então é:

echo '[a' '' 'b]'
args(){ echo ["$1"]["$2"]; }
args a   :b  # three spaces
# [a][:b]

não é surpresa como não dividir palavras.

args(){ echo [$1][$2]; }
args a   :b  # three spaces
# [a][ b]   

Isso é como:

 echo '[a]' '[' 'b]'

como $2 ( :b ) seria dividido na string vazia e b .

Um caso em que você verá variações entre implementações é quando $IFS está vazio.

Em:

set a b
IFS=
printf '<%s>\n' $*

Em algumas conchas (a maioria hoje em dia), você vê

<a>
<b>

E não <ab> , embora "$*" seja expandido para ab . Esses shells ainda separam os parâmetros a e b position e agora foi feito um requisito POSIX na última versão do padrão.

Se você fez:

set a b
IFS=
var="$*" # note that the behaviour for var=$* is unspecified
printf '<%s>\n' $var

você veria <ab> como as informações que a e b eram 2 argumentos separados foram perdidos quando atribuídos a $var .

¹, é claro, não são apenas espaços em branco que delimitam palavras. Tokens especiais na sintaxe do shell também, cuja lista depende do contexto. Na maioria dos contextos, | , || , & , ; , nova linha, < , > , >> ... delimitam palavras. Em ksh93 , por exemplo, você pode escrever um comando em branco como:

while({([[(:)]])})&&((1||1))do(:);uname<&2|tee>(rev)file;done
    
por 03.12.2017 / 23:36