Используя, “в то время как считано …”, эхо и printf получают различные результаты

Согласно этому вопросу "Используя, "в то время как считано..." в сценарии Linux"

echo '1 2 3 4 5 6' | while read a b c;do echo "$a, $b, $c"; done

результат:

1, 2, 3 4 5 6

но когда я заменяю echo с printf

echo '1 2 3 4 5 6' | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done

результат

1, 2, 3
4, 5, 6

Кто-то мог сказать мне, что делает эти две команды отличающимися? Спасибо ~

14
задан 22 July 2017 в 02:08

2 ответа

Это не просто эхо по сравнению с printf

Во-первых, давайте поймем то, что происходит с read a b c часть. read выполнит разделение слова на основе значения по умолчанию IFS переменная, которая является новой строкой вкладки пространства и соответствует всему на основе этого. Если там будет более введен, чем переменные для содержания его, то это вместит splitted части в первые переменные, и что не может быть приспособлено - войдет в последний раз. Вот то, что я имею в виду:

bash-4.3$ read a b c <<< "one two three four"
bash-4.3$ echo $a
one
bash-4.3$ echo $b
two
bash-4.3$ echo $c
three four

Это точно, как это описано в bashруководство (см. кавычку в конце ответа).

В Вашем случае, что происходит, то, что, 1 и 2 вписывается в a и b переменные, и c берет все остальное, которое является 3 4 5 6.

Что Вы также будете видеть, что много времен - тот, люди используют while IFS= read -r line; do ... ; done < input.txt считать текстовые файлы линию за линией. Снова, IFS= здесь по причине для разделения управляющего слова, или более конкретно - отключают его и читают одну строку текста в переменную. Если это не было там, read попытался бы соответствовать каждому отдельному слову в line переменная. Но это - другая история, которую я поощряю Вас изучить позже с тех пор while IFS= read -r variable очень часто используемая структура.

эхо по сравнению с printf поведением

echo делает то, что Вы ожидали бы здесь. Это отображает Ваши переменные точно как read расположил их. Это было уже продемонстрировано в предыдущем обсуждении.

printf является совершенно особым, потому что это продолжит вмещать переменные в строку формата, пока все они не будут исчерпаны. Таким образом, когда Вы делаете printf "%d, %d, %d \n" $a $b $c printf видит строку формата с 3 десятичными числами, но существует больше аргументов, чем 3 (потому что Ваши переменные на самом деле расширяются до индивидуума 1,2,3,4,5,6). Это может звучать сбивающим с толку, но существует по причине как улучшенное поведение от какой реальное printf() функция делает на языке C.

То, что Вы также сделали здесь, который влияет на вывод, - то, что Ваши переменные не заключаются в кавычки, который позволяет оболочку (нет printf ) сломать переменные в 6 отдельных объектов. Сравните это с заключением в кавычки:

bash-4.3$ read a b c <<< "1 2 3 4"
bash-4.3$ printf "%d %d %d\n" "$a" "$b" "$c"
bash: printf: 3 4: invalid number
1 2 3

Точно, потому что $c переменная заключается в кавычки, она теперь распознана как одна целая строка, 3 4, и это не соответствует %d формат, который является просто единственным целым числом

Теперь сделайте то же без заключения в кавычки:

bash-4.3$ printf "%d %d %d\n" $a $b $c
1 2 3
4 0 0

printf снова говорит: "Хорошо, у Вас есть 6 объектов там, но формат показывает только 3, таким образом, я буду продолжать соответствовать материалу и оставлять незаполненный независимо от того, что я не могу соответствовать к фактическому входу от пользователя".

И во всех этих случаях Вы не имеете к честное слово. Просто выполненный strace -e trace=execve и лично убедитесь, что делает команду, на самом деле "см.":

bash-4.3$ strace -e trace=execve printf "%d %d %d\n" $a $b $c
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3", "4"], [/* 80 vars */]) = 0
1 2 3
4 0 0
+++ exited with 0 +++

bash-4.3$ strace -e trace=execve printf "%d %d %d\n" "$a" "$b" "$c"
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3 4"], [/* 80 vars */]) = 0
1 2 printf: ‘3 4’: value not completely converted
3
+++ exited with 1 +++

Дополнительные примечания

Поскольку Charles Duffy правильно указал в комментариях,bash имеет его собственное встроенное printf, который является тем, что Вы используете в своей команде, strace будет на самом деле звонить /usr/bin/printf версия, не версия оболочки. Кроме незначительных различий, для нашего интереса к этому конкретному вопросу спецификаторы стандартного формата являются тем же, и поведение является тем же.

То, что также должно иметься в виду, является этим printf синтаксис является намного более портативным (и поэтому предпочтенный), чем echo, не говоря уже о том, что синтаксис более знаком C или любому подобному языку C, который имеет printf() функция в нем. См. этот превосходный ответ terdon на предмет printf по сравнению с echo. В то время как можно сделать вывод адаптированным в соответствии с определенной оболочкой на определенной версии Ubuntu, если Вы собираетесь быть сценариями портирования через различные системы, вероятно, необходимо предпочесть printf а не эхо. Возможно, Вы - системный администратор новичка, работающий с Ubuntu и машинами CentOS или возможно даже FreeBSD - кто знает - так в таких случаях, необходимо будет сделать выбор.

Кавычка из руководства удара, SHELL ВСТРОЕННЫЙ раздел COMMANDS

читайте [-ers] [-a aname] [-d delim] [-i текст] [-n nchars] [-N nchars] [-p подсказка] [-t тайм-аут] [-u fd] [имя...]

Одна строка читается из стандартного входа, или из дескриптора файла fd предоставленный как аргумент-u опции, и первое слово присвоено имени, второму слову к второму имени, и так далее, с оставшимися словами и их вмешательством separa ‐ скалистые вершины, присвоенные фамилии. Если существует меньше слов, считанных из входного потока, чем имена, остающимся именам присваивают пустые значения. Символы в IFS используются для разделения строки на слова с помощью тех же правил использование оболочки для расширения (описанный выше в соответствии с Word Splitting).

24
ответ дан 23 November 2019 в 02:54

Это - только предложение и не предназначенное для замены ответа Sergiy вообще. Я думаю, что Sergiy записал большой ответ относительно того, почему они отличаются в печати. Как переменная на чтении присвоена с остающимся в $c переменная как 3 4 5 6 после 1 и 2 присваивают a и b. echo не разделит переменную для Вас где printf будет с %ds.

Можно, однако, заставить их в основном дать Вам те же ответы путем управления эхом чисел в начале команды:

В /bin/bash можно использовать:

echo -e "1 2 3 \n4 5 6"

В /bin/sh можно просто использовать:

echo "1 2 3 \n4 5 6"

Bash использует-e для включения \символы ESC, где sh не нужен он, поскольку он уже включен. \n причины это для создания новой строки поэтому теперь строка эха разделяется на две отдельных строки, которые могут теперь использоваться два раза для инструкции циклов эха:

:~$ echo -e "1 2 3 \n4 5 6" | while read a b c; do echo "$a, $b, $c"; done
1, 2, 3
4, 5, 6

Который в свою очередь производит тот же вывод с использованием printf команда:

:~$ echo -e "1 2 3 \n4 5 6" | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done
1, 2, 3 
4, 5, 6 

В sh

$ echo "1 2 3 \n4 5 6" | while read a b c; do echo "$a, $b, $c"; done
1, 2, 3
4, 5, 6
$ echo "1 2 3 \n4 5 6" | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done
1, 2, 3 
4, 5, 6 

Надеюсь, это поможет!

7
ответ дан 23 November 2019 в 02:54

Другие вопросы по тегам:

Похожие вопросы: