Por que printf imprime mais argumentos do que o esperado?

9

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

Eu esperava que o script ignorasse as entradas após 5.

Roteiro:

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

Resultado:

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 estiver 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"

Resultado:

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:~$ 
mikeserv
fonte
pare printfa qualquer momento com o \cescape associado a um %bespecificador de formato. Como:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv,

Respostas:

18

Você tem três problemas:

  1. Com read, se houver menos nomes de variáveis ​​que campos na entrada, a última var será vinculada a todos os campos restantes na linha, com delimitadores. Isso significa que $eentra 5 6no seu primeiro exemplo inesperado.
  2. Como todos $a.. $enão estão entre aspas, seus valores passam por uma divisão de campo . Se $econtém " 5 6", ele se expande em dois argumentos para o comando.
  3. printfconsome todos os seus argumentos, usando o mesmo número de argumentos ao mesmo tempo que houver %substituições, repetidamente. Isso está oculto na documentação como:

    O formatoperando deve ser reutilizado quantas vezes for necessário para satisfazer os operandos do argumento. Quaisquer especificadores extras cou de sconversão devem ser avaliados como se um argumento de cadeia nula fosse fornecido; outras especificações extras de conversão devem ser avaliadas como se um argumento zero fosse fornecido.

    Em outras palavras, se houver argumentos não utilizados, ele recomeça e os processa também desde o início, incluindo toda a cadeia de formato. Isso é útil quando você deseja formatar uma matriz inteira, diga:

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

    Seu printfcomando obtém um argumento de cada um de $a... $de, no entanto, muitos são os que restam $e. Quando $eé " 5 6", printftem duas voltas, a segunda apenas começando 6a formatar. Quando é 5 6 7 8 9 10, possui toda a gama de substituições para a segunda impressão.


Você pode evitar tudo isso adicionando um campo fictício extra a reade citando suas substituições de parâmetro (o que é sempre uma boa idéia):

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

Isso dará:

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

dummyobtém todos os campos extras e printfsomente os cinco argumentos que você esperava.


Sua segunda pergunta editada tem uma resposta semelhante: só aobtém um valor quando IFSnão há espaço. Isso significa $b... $eexpandir para nada, então printfapenas recebe um único argumento. Seus espaços da string de formato são impressos, sem nada substituído entre eles ("como se um argumento de string nulo fosse fornecido").

Michael Homer
fonte
Testei novamente o segundo script usando "$ a" ..... "$ e". O segundo script está dando o mesmo problema novamente.
3
A citação não fará diferença no segundo script. atem o valor 1 2 3 4 5como uma única sequência e é substituído de uma só vez.
Michael Homer
6
 printf "> %s < " 1 2 3

irá imprimir

 > 1 <> 2 <> 3 <

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

impressões

 > 1 2 <> 3  <

printf utiliza todos os argumentos para satisfazer sua cadeia de formatação e depois se repete até que todos os argumentos sejam processados.

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


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

... O formato é reutilizado conforme necessário para consumir todos os argumentos. Se houver menos argumentos do que o formato exige, especificações extras de formato se comportarão como se um valor zero ou uma seqüência nula, conforme apropriado, tivesse sido fornecida. ...

e é mandatado por http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

PSkocik
fonte
Por que esse comportamento? está documentado?
Shiplu Mokaddim
1
A @Shiplu adicionou um parágrafo sobre onde o comportamento está documentado e o padrão que exige o comportamento.
PSKocik