Согласно этому вопросу "Используя, "в то время как считано..." в сценарии 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
Кто-то мог сказать мне, что делает эти две команды отличающимися? Спасибо ~
Во-первых, давайте поймем то, что происходит с 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
очень часто используемая структура.
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 - кто знает - так в таких случаях, необходимо будет сделать выбор.
читайте [-ers] [-a aname] [-d delim] [-i текст] [-n nchars] [-N nchars] [-p подсказка] [-t тайм-аут] [-u fd] [имя...]
Одна строка читается из стандартного входа, или из дескриптора файла fd предоставленный как аргумент-u опции, и первое слово присвоено имени, второму слову к второму имени, и так далее, с оставшимися словами и их вмешательством separa ‐ скалистые вершины, присвоенные фамилии. Если существует меньше слов, считанных из входного потока, чем имена, остающимся именам присваивают пустые значения. Символы в IFS используются для разделения строки на слова с помощью тех же правил использование оболочки для расширения (описанный выше в соответствии с Word Splitting).
Это - только предложение и не предназначенное для замены ответа Sergiy вообще. Я думаю, что Sergiy записал большой ответ относительно того, почему они отличаются в печати. Как переменная на чтении присвоена с остающимся в $c
переменная как 3 4 5 6
после 1
и 2
присваивают a
и b
. echo
не разделит переменную для Вас где printf
будет с %d
s.
Можно, однако, заставить их в основном дать Вам те же ответы путем управления эхом чисел в начале команды:
В /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
Надеюсь, это поможет!