Por que o printf imprime mais argumentos do que o esperado?

8

Por que este script de shell imprime as entradas duas vezes?

Eu esperava que o script ignorasse as entradas depois de 5.

Script:

#! /bin/bash
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e

Saída:

user@linux:~$ pico ifs2.sh
user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 
> 1 2 3 4 5 <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6
> 1 2 3 4 5 <> 6     <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6 7 8 9 0
> 1 2 3 4 5 <> 6 7 8 9 0 <user@linux:~$ 

E o script a seguir funciona, independentemente do que esteja definido como $ IFS. Por quê?

#! /bin/bash    
old="$IFS"
IFS=":"
echo "IFS = $IFS"
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e    
IFS="$old"

Saída:

user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5  
> 1 2 3 4 5      <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5
> 1 2 3 4 5     <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1:2:3:4:5
> 1 2 3 4 5 <user@linux:~$ 
    
por mikeserv 21.06.2015 / 01:27

2 respostas

16

Você tem três problemas:

  1. Com read , se houver menos nomes de variáveis do que campos na entrada, o último var será vinculado a todos os campos restantes na linha, com delimitadores. Isso significa que $e obtém 5 6 em seu primeiro exemplo inesperado.
  2. Como todos os $a .. $e não estão registrados, seus valores passam por divisão de campo . Se $e detiver " 5 6 ", ele será expandido em dois argumentos para o comando.
  3. printf consome todos os seus argumentos, usando tantos argumentos de uma só vez quanto há % substituições, repetidamente. Isso é inserido na documentação como:

    The format operand shall be reused as often as necessary to satisfy the argument operands. Any extra c or s conversion specifiers shall be evaluated as if a null string argument were supplied; other extra conversion specifications shall be evaluated as if a zero argument were supplied.

    Em outras palavras, se houver argumentos não utilizados, ele será iniciado novamente e processado também desde o início, incluindo toda a cadeia de formatações. Isso é útil quando você deseja formatar uma matriz inteira, por exemplo:

    printf '%b ' "${array[@]}"
    

    Seu comando printf obtém um argumento de cada um dos $a .. $d e, em seguida, quantos sobram de $e . Quando $e for " 5 6 ", printf terá duas, a segunda apenas 6 será formatada. Quando é 5 6 7 8 9 10 , tem toda a gama de substituições para a segunda impressão.

Você pode evitar tudo isso adicionando um campo dummy extra a read e citando suas substituições de parâmetro (o que é sempre uma boa ideia):

read  a b c d e dummy
printf "> %s %s %s %s %s <" "$a" "$b" "$c" "$d" "$e"

Isso vai dar:

Enter 5 words : 
1 2 3 4 5 6 7 8 9 10
> 1 2 3 4 5 <

dummy obtém todos os campos extras e printf obtém apenas os cinco argumentos esperados.

Sua segunda pergunta editada tem uma resposta semelhante: somente a obtém um valor quando IFS não tem espaço. Isso significa $b .. $e expandir para nada, então printf obtém apenas um único argumento. Seus espaços da string de formatação são impressos, sem nada substituído entre eles ("como se um argumento de string nula fosse fornecido").

    
por 21.06.2015 / 01:49
6
 printf "> %s < " 1 2 3

imprimirá

 > 1 <> 2 <> 3 <
  printf "> %s %s <" 1 2 3

imprime

 > 1 2 <> 3  <

printf consome todos os argumentos para satisfazer sua string de formato e repete até que todos os argumentos sejam processados.

O segundo script funciona porque apenas $a é atribuído e, portanto, o comando não transborda para iterações adicionais (existe apenas uma iteração).

Esse comportamento está documentado no texto fornecido com help printf :

... The format is re-used as necessary to consume all of the arguments. If there are fewer arguments than the format requires, extra format specifications behave as if a zero value or null string, as appropriate, had been supplied. ...

e é obrigatório pelo link

    
por 21.06.2015 / 01:45