Почему printf лучше, чем эхо?

Я слышал, что printf лучше, чем echo. Я могу вспомнить только один случай из моего опыта, когда мне пришлось использовать printf, потому что echo не работал для подачи некоторого текста в какую-либо программу на RHEL 5.8, но printf сделал. Но, по-видимому, есть и другие различия, и я хотел бы узнать, что они есть, а также, если есть конкретные случаи, когда использовать один против другого.

484 голоса | спросил amphibient 22 FebruaryEurope/MoscowbFri, 22 Feb 2013 23:49:06 +0400000000pmFri, 22 Feb 2013 23:49:06 +040013 2013, 23:49:06

4 ответа


667

В принципе, это проблема переносимости (и надежности).

Изначально echo не принимал никакой опции и ничего не расшифровывал. Все, что он делал, это вывод его аргументов, разделенных символом пробега, и завершение символом новой строки.

Теперь кто-то подумал, что было бы неплохо, если бы мы могли делать что-то вроде echo "\n\t" для вывода символов новой строки или табуляции или иметь возможность не выводить символ новой строки.

Затем они подумали сложнее, но вместо добавления этой функциональности в оболочку (например, perl), где внутри двойных кавычек \t на самом деле означает символ табуляции), они добавили его на echo.

Дэвид Корн осознал ошибку и ввел новую форму кавычек оболочки: $'...', который позже был скопирован с помощью bash и zsh code>, но к тому времени было слишком поздно.

Теперь, когда стандартный UNIX echo получает аргумент, который содержит два символа \ и t, вместо вывода их, он выдает символ табуляции. И как только он увидит в аргументе \c, он перестает выводить (так что конечная новая строка также не выводится).

Другие оболочки /производители /версии Unix решили сделать это по-другому: они добавили параметр -e для расширения управляющих последовательностей и -n, чтобы не выводить конец новой строки. У некоторых есть -E, чтобы отключить escape-последовательности, некоторые из них имеют -n, но не -e, список управляющих последовательностей, поддерживаемых одним echo не обязательно совпадает с другой.

Sven Mascheck имеет приятную страницу, которая показывает степень проблемы .

В тех реализациях echo, которые поддерживают параметры, обычно нет поддержки echo, чтобы отметить конец опций (zsh и, возможно, другие поддерживают -- для этого, хотя), поэтому, например, трудно выполнить вывод - с "-n" во многих оболочках.

На некоторых оболочках, таких как echo 1 или bash 2 или ksh93 yash), поведение даже зависит от того, как была скомпилирована оболочка или окружающей среды (поведение GNU $ECHO_STYLE) также изменится, если echo находится в среде, $POSIXLY_CORRECT с ее опцией zsh, некоторые pdksh-based с их параметром bsd_echo или они вызываются как posix или нет). Таким образом, два sh bash s, даже из той же версии echo, не гарантируют себя одинаковыми.

POSIX говорит: , если первый аргумент bash, или любой аргумент содержит обратную косую черту, тогда поведение неуказано . -n эхо в этом отношении не является POSIX в том, что, например, bash не выводит echo -e как требуется POSIX. Спецификация UNIX более строгая, она запрещает -e<newline> и требует расширения некоторых управляющих последовательностей, включая -n, чтобы остановить вывод.

Эти спецификации действительно не приходят сюда на помощь, поскольку многие реализации не соответствуют требованиям. Даже некоторые сертифицированные системы , такие как macOS 4 , не соответствуют требованиям.

Чтобы действительно представить текущую реальность, POSIX должен фактически сказать: , если первый аргумент соответствует расширенному регулярному выражению \c или любому аргумент содержит обратную косую черту (или символы, кодировка которых содержит кодировку символа обратной косой черты, такой как ^-([eEn]*|-help|-version)$ в локалях с использованием кодировки BIG5), тогда поведение не указано.

В общем, вы не знаете, что будет выводить α, если вы не можете убедиться, что echo "$var" не содержит символы обратной косой черты и doesn 't начинаем с $var. В этом случае спецификация POSIX на самом деле говорит нам использовать -.

Так что это означает, что вы не можете использовать printf для отображения неконтролируемых данных. Другими словами, если вы пишете скрипт и берете внешний вход (от пользователя в качестве аргументов или имена файлов из файловой системы ...), вы не можете использовать echo для отобразите его.

Это нормально:

echo

Это не:

echo >&2 Invalid file.

(Хотя он будет работать с некоторыми (не совместимыми с UNIX) реализациями echo >&2 "Invalid file: $file" , такими как echo, когда параметр bash так или иначе, как во время компиляции или через среду).

xpg_echo не подходит для большинства реализаций (исключения file=$(echo "$var" | tr ' ' _) с помощью yash (с оговоркой, чтоECHO_STYLE=raw не могут содержать произвольные последовательности байтов, а не произвольные имена файлов) и yash 'zsh).

echo -E - "$var", с другой стороны, является более надежным, по крайней мере, когда он ограничен основным использованием printf.

echo

Выведет содержимое printf '%s\n' "$var" , за которым следует символ новой строки, независимо от того, какой символ он может содержать.

$var

Выведет его без символа новой строки.

Теперь существуют также различия между реализациями printf '%s' "$var" . Существует множество функций, которые задаются POSIX, но есть много расширений. Например, некоторые поддерживают printf, чтобы процитировать аргументы, но как это делается, варьируется от оболочки к оболочке, некоторые поддерживают %q для символов Unicode. Поведение меняется для \uxxxx в многобайтовых локалях, для printf '%10s\n' "$var" есть как минимум три разных результата:

Но в конце, если вы придерживаетесь набора функций POSIX printf %b '\123' и не пытайтесь делать что-то слишком необычное, вы не в безопасности.

Но помните, что первым аргументом является формат, поэтому он не должен содержать переменные /неконтролируемые данные.

Более надежный printf может быть реализован с помощью echo, например:

printf

Подлеую оболочку (которая предполагает порождение дополнительного процесса в большинстве реализаций оболочки) можно избежать, используя echo() ( # subshell for local scope for $IFS IFS=" " # needed for "$*" printf '%s\n' "$*" ) echo_n() ( IFS=" " printf %s "$*" ) echo_e() ( IFS=" " printf '%b\n' "$*" ) со многими оболочками или написав ее так:

local IFS

Примечания

1. как поведение echo() { if [ "$#" -gt 0 ]; then printf %s "$1" shift fi if [ "$#" -gt 0 ]; then printf ' %s' "[email protected]" fi printf '\n' } bash может быть изменено.

С echo во время выполнения есть две вещи, которые управляют поведением или bash (рядом с echo) или переопределением echo как функция или псевдоним): параметр enable -n echo echo и находится ли xpg_echo в режиме posix. bash можно включить, если bash вызывается как posix или если bash находится в среде или с sh:

Поведение по умолчанию для большинства систем:

POSIXLY_CORRECT

posix расширяет последовательности, как требуется UNIX:

$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character

Он по-прежнему соблюдает xpg_echo и $ BASHOPTS=xpg_echo bash -c 'echo "\0101"' A -n):

-e

С помощью -E и режима POSIX:

$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%

На этот раз xpg_echo является одновременно совместимым с POSIX и UNIX. Обратите внимание, что в режиме POSIX $ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"' -n A $ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash) -n A $ env BASHOPTS=xpg_echo SHELLOPTS=posix ARGV0=sh bash -c 'echo -n "\0101"' -n A $ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"' -n A по-прежнему не соответствует POSIX, поскольку он не выводит bash в:

bash

Значения по умолчанию для xpg_echo и posix могут быть определены во время компиляции с -e и $ env SHELLOPTS=posix bash -c 'echo -e' $ для сценария --enable-xpg-echo-default. Обычно это то, что делают последние версии OS /X для создания своих --enable-strict-posix-default. Никакая реализация /распределение Unix /Linux в правильном порядке, как правило, не используется для configure.

2. Как поведение /bin/sh /bin/bash может быть изменено.

В ksh93, если echo расширяет escape-последовательности или нет и распознает параметры, зависит от содержимого ksh93.

Если echo содержит компонент, содержащий $PATH или $PATH до /5bin или /usr /bin, тогда он ведет себя по пути SysV /UNIX (расширяет последовательности, не принимает параметры). Если он сначала найдет /xpg или /bin, тогда он ведет BSD 3 способ (/usr/bin), чтобы включить расширение, распознает /ucb). По умолчанию системная зависимость, BSD на Debian:

/bsd

3. BSD для echo -e?

Ссылка на BSD для обработки опции -e немного вводит в заблуждение. Большинство этих разных и несовместимых действий -n были представлены в лабораториях Bell:

  • $ ksh93 -c 'echo -n' # default -> BSD (on Debian) $ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG -n $ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG -n $ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD $ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD , -e, echo в Work Bench UNIX (на основе Unix V6), а остальные (\n, \0ooo ...) в Unix System III Ссылка .
  • \c в Unix V7 (by Denis Ritchie Ref )
  • \b в Unix V8 (by Denis Ritchie Ref )
  • \r сам по себе изначально появился из -n (CWRU /CWRU.chlog в версия 1.13.5 упоминает Брайана Фокса, добавив его в 1992-10-18 гг., GNU -e скопировал его в ближайшее время после того, как в sh-utils-1.8 выпущен через 10 дней)

В то время как встроенный -E из bash или BSD поддерживал echo с того дня, как они начали использовать оболочку Almquist для нее начале 90-х годов автономная утилита echo по сей день не поддерживает ее там ( FreeBSD sh по-прежнему не поддерживает -e, хотя он поддерживает echo как Unix V7 (а также echo, но только в конце последнего аргумента)).

Обработка -e была добавлена ​​в -n \c, когда в BSD юниверс в версия ksh93r, выпущенная в 2006 году и может быть отключена во время компиляции.

4. macOS -e

Большинство версий macOS получили сертификацию UNIX из OpenGroup .

Их ksh93 встроенный echo соответствует, так как это echo (очень старая версия), встроенная с sh enabled по умолчанию, но их автономная утилита echo отсутствует. bash выводит ничего вместо xpg_echo, echo выводит env echo -n вместо -n<newline>.

То, что env echo '\n' - это имя из FreeBSD, которое подавляет вывод новой строки, если первый аргумент \n<newline> или (с 1995 года), если последний аргумент заканчивается на \ c, но не поддерживает любые другие последовательности обратного слэш, требуемые UNIX, даже не <newline><newline>. Это означает, что не только сложно, но и невозможно получить в одном вызове вывод /bin/echo или -n.

ответил Stéphane Chazelas 23 FebruaryEurope/MoscowbSat, 23 Feb 2013 01:31:32 +0400000000amSat, 23 Feb 2013 01:31:32 +040013 2013, 01:31:32
25

Вы можете использовать printf для своих параметров форматирования. echo полезно, когда дело доходит до печати значения переменной или (простой) строки, но это все, что нужно. printf может в основном делать то, что может сделать версия C.

Пример использования и возможностей:

Echo:

echo "*** Backup shell script ***"
echo
echo "Runtime: $(date) @ $(hostname)"
echo

printf:

vech="bike"
printf "%s\n" "$vech"

Источники:

ответил NlightNFotis 23 FebruaryEurope/MoscowbSat, 23 Feb 2013 00:13:07 +0400000000amSat, 23 Feb 2013 00:13:07 +040013 2013, 00:13:07
15

Одно «преимущество», если вы хотите назвать это, было бы так, что вам не нужно говорить ему, как echo интерпретировать определенные escape-последовательности, такие как \n. Он знает, как их интерпретировать, и для этого не требуется -e.

printf "some\nmulti-lined\ntext\n"

(NB: последний \n необходим, echo подразумевает его, если вы не указали параметр -n)

против

echo -e "some\nmulti-lined\ntext"

Обратите внимание на последний \n в printf. В конце концов, это вопрос вкуса и требований , что вы используете: echo или printf .

ответил 0xC0000022L 23 FebruaryEurope/MoscowbSat, 23 Feb 2013 00:21:50 +0400000000amSat, 23 Feb 2013 00:21:50 +040013 2013, 00:21:50
6

Один недостаток printf - это производительность, потому что встроенная оболочка echo выполняется намного быстрее. Это особенно важно в Cygwin, где каждый экземпляр новой команды вызывает тяжелые накладные расходы Windows. Когда я изменил свою эхо-тяжелую программу с помощью /bin/echo для эха оболочки, производительность почти удвоилась. Это компромисс между переносимостью и производительностью. Это не slam dunk, чтобы всегда использовать printf.

ответил John 3 +04002014-10-03T21:00:12+04:00312014bEurope/MoscowFri, 03 Oct 2014 21:00:12 +0400 2014, 21:00:12

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132