В основному, це питання портативності (та надійності).
Спочатку echo
не прийняв жодного варіанту і нічого не розширив. Все, що вона робила, було виводити свої аргументи, розділені пробільним символом та завершені символом нового рядка.
Тепер хтось подумав, що було б непогано, якби ми могли зробити такі речі, як echo "\n\t"
виведення символів нового рядка чи вкладки, або мати можливість не виводити символ нового рядка.
Потім вони думали важче, але замість того, щоб додавати цю функціональність до оболонки (наприклад, perl
де всередині подвійних лапок, \t
насправді означає символ вкладки), вони додали її echo
.
Девід Корн зрозумів помилку і ввів нову форму лапки оболонки: $'...'
яка пізніше була скопійована bash
і , zsh
але це було вже занадто пізно до того часу.
Тепер, коли стандартний UNIX echo
отримує аргумент, який містить два символи, \
і t
замість того, щоб виводити їх, він виводить символ табуляції. І як тільки він помічається \c
в аргументі, він припиняє виводити (тож, новий вигляд рядка також не виводиться).
Інші оболонки / постачальники / версії Unix вирішили зробити це інакше: вони додали -e
опцію розширення послідовностей евакуації та -n
опцію не виводити проміжний новий рядок. Деякі мають -E
відключити послідовності відходу, деякі мають, -n
але ні -e
, перелік послідовностей евакуації, що підтримується однією echo
реалізацією, не обов'язково такий же, як підтримуваний іншим.
У Свена Машека є приємна сторінка, яка показує масштаб проблеми .
У тих echo
реалізаціях , які підтримують варіанти, немає взагалі ніякої підтримки з боку , --
щоб відзначити кінець опцій (на echo
вбудовану команду декого не-Борн-подібних оболонок роблять, і ЗШ підтримує -
для цього , хоча), так, наприклад, це важко вивести "-n"
з echo
в багато снарядів.
На деяких оболонках, таких як bash
¹ або ksh93
² або yash
( $ECHO_STYLE
змінної), поведінка навіть залежить від того, як складено оболонку або оточення ( echo
поведінка GNU також зміниться, якщо $POSIXLY_CORRECT
вона знаходиться в оточенні та з версією 4 , zsh
з її bsd_echo
можливістю, деякі на основі pdksh з їхньою posix
опцією, чи вони називаються як sh
ні). Тож два bash
echo
s, навіть з тієї ж версії bash
, не гарантовано поводяться однаково.
POSIX говорить: якщо перший аргумент є -n
або будь-який аргумент містить зворотні косої риси, то поведінка не визначено . bash
відлуння в цьому плані не POSIX, оскільки, наприклад echo -e
, не виводиться так, -e<newline>
як вимагає POSIX. Специфікація UNIX суворіша, вона забороняє -n
і вимагає розширення деяких послідовностей евакуації, включаючи таку, \c
щоб зупинити виведення.
Ці специфікації насправді не допомагають, враховуючи, що багато реалізацій не відповідають стандартам. Навіть деякі сертифіковані системи, такі як macOS 5 , не відповідають стандартам.
Щоб справді представити поточну реальність, POSIX повинен насправді сказати : якщо перший аргумент відповідає ^-([eEn]*|-help|-version)$
розширеному регулярному виводу або будь-який аргумент містить зворотні косої риси (або символи, кодування яких містить кодування символу зворотної косої риси, як α
у локалі за допомогою діаграми BIG5), тоді поведінка невизначений.
Загалом, ви не знаєте, що echo "$var"
вийде, якщо не переконаєтесь, що $var
він не містить символів зворотної косої риси і не починається з цього -
. Специфікація POSIX насправді говорить про те, щоб використовувати printf
в цьому випадку замість цього.
Отже, це означає, що ви не можете використовувати echo
для відображення неконтрольованих даних. Іншими словами, якщо ви пишете сценарій, і він приймає зовнішній вхід (від користувача у вигляді аргументів або імен файлів з файлової системи ...), ви не можете використовувати його echo
для відображення.
Це нормально:
echo >&2 Invalid file.
Це не:
echo >&2 "Invalid file: $file"
(Хоча це буде добре працювати з деякими (не сумісними з UNIX) echo
реалізаціями, такими як bash
's, коли xpg_echo
параметр не було включено так чи інакше, як під час компіляції або через середовище).
file=$(echo "$var" | tr ' ' _)
НЕ в порядку в більшості реалізацій (виняток становлять yash
з ECHO_STYLE=raw
(з одним застереженням , що yash
«и змінні не можуть займати довільні послідовності байтів , так не довільні імена файлів) і zsh
" S echo -E - "$var"
6 ).
printf
, з іншого боку, більш надійний, принаймні, коли він обмежений базовим використанням echo
.
printf '%s\n' "$var"
Виведе вміст $var
наступного символу нового рядка незалежно від того, який символ він може містити.
printf '%s' "$var"
Виведе його без символу нового рядка.
Тепер також існують відмінності між printf
реалізаціями. Існує ядро функцій, яке визначено POSIX, але тоді є багато розширень. Наприклад, деякі підтримують a %q
для цитування аргументів, але те, як це робиться, варіюється від оболонки до оболонки, деякі підтримують \uxxxx
символи unicode. Поведінка різниться printf '%10s\n' "$var"
в багатобайтових локалях, для цього є щонайменше три різні результатиprintf %b '\123'
Зрештою, якщо ви дотримуєтесь набору функцій POSIX 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
з багатьма оболонками, або записавши її так:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Примітки
1. як bash
«s echo
поведінка може бути змінено.
З bash
, під час виконання, є дві речі, які керують поведінкою echo
(поряд enable -n echo
або переосмисленням echo
функції або псевдоніма): xpg_echo
bash
опція та чи bash
знаходиться в режимі posix. posix
режим можна ввімкнути, якщо bash
викликається як sh
або якщо POSIXLY_CORRECT
знаходиться в оточенні або з posix
опцією:
Поведінка за замовчуванням у більшості систем:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
розширює послідовності, як вимагає UNIX:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Він все ще вшановує -n
та -e
(і -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
З xpg_echo
режимом і 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 bash -c 'echo -n "\0101"'
-n A
Цього разу bash
сумісність відповідає POSIX та UNIX. Зауважте, що в режимі POSIX bash
все ще не відповідає POSIX, оскільки він не виводиться -e
в:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Значення за замовчуванням для xpg_echo і POSIX можуть бути визначені під час компіляції з --enable-xpg-echo-default
і --enable-strict-posix-default
опції до configure
сценарієм. Це зазвичай те, що останні версії OS / X роблять для створення своїх /bin/sh
. Ні Unix / Linux реалізації / розподілу в здоровому глузді не буде , як правило , зробити це для /bin/bash
хоча . Насправді, це неправда, те, /bin/bash
що Oracle поставляється з Solaris 11 (в додатковому пакеті), здається, побудовано з --enable-xpg-echo-default
(це не було в Solaris 10).
2. Як ksh93
«s echo
поведінка може бути змінено.
У ksh93
тому, echo
розширює послідовність виходу чи ні та розпізнає параметри, залежить від вмісту змінних $PATH
та / або $_AST_FEATURES
середовища.
Якщо $PATH
містить компонент, який містить /5bin
або /xpg
перед /bin
або /usr/bin
компонент, то він веде себе способом SysV / UNIX (розширює послідовності, не приймає параметри). Якщо він знаходить /ucb
або /bsd
спочатку або якщо $_AST_FEATURES
7 містить UNIVERSE = ucb
, то він поводить BSD 3 спосіб ( -e
щоб увімкнути розширення, розпізнає -n
).
За замовчуванням залежить система, BSD на Debian (див. Вихід builtin getconf; getconf UNIVERSE
у останніх версіях ksh93):
$ 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=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ 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
3. BSD для ехо -е?
Посилання на BSD для обробки -e
опції тут трохи оманливе. Більшість цих різних і несумісних echo
поведінки були представлені на AT&T:
\n
, \0ooo
, \c
В верстаті програміста UNIX (на базі Unix V6), а решта ( \b
, \r
...) в Unix System III Ref .
-n
в Unix V7 (від Dennis Ritchie Ref )
-e
в Unix V8 (від Dennis Ritchie Ref )
-E
сам, можливо, спочатку походив bash
(CWRU / CWRU.chlog у версії 1.13.5 згадує Брайана Фокса, додаючи його 1992-10-18, GNU echo
копіює його незабаром після того, як в sh-utils-1.8 випущено 10 днів пізніше)
Хоча echo
вбудовані sh
або BSD підтримуються -e
з дня, коли вони почали використовувати оболонку Almquist для неї на початку 90-х, автономна echo
утиліта до цього дня не підтримує її ( FreeBSDecho
досі не підтримує -e
, хоча вона підтримує, -n
як Unix V7 (і також, \c
але лише в кінці останнього аргументу)).
Обробка -e
була додана до ksh93
's, echo
коли у Всесвіті BSD у версії ksh93r, випущеній у 2006 році, її можна відключити під час компіляції.
4. Зміни поведінки GHU у 8.31
Оскільки Coreutils 8,31 (і це робить ), GNU echo
Тепер розширює керуючі послідовності за замовчуванням , коли POSIXLY_CORRECT знаходиться в навколишньому середовищі, відповідно до поведінкою bash -o posix -O xpg_echo
«и echo
вбудованої команди (див повідомлення про помилку ).
5. macOS echo
Більшість версій macOS отримали сертифікат UNIX від OpenGroup .
Їх sh
вбудований echo
сумісний, оскільки він bash
(дуже стара версія) побудований із xpg_echo
включеним за замовчуванням, але їх автономна echo
утиліта - ні. env echo -n
виводить нічого замість -n<newline>
, env echo '\n'
виводить \n<newline>
замість <newline><newline>
.
Це /bin/echo
той з FreeBSD, який пригнічує вихід нового рядка, якщо перший аргумент є -n
або (з 1995 р.), Якщо останній аргумент закінчується \c
, але не підтримує жодних інших послідовностей зворотних рисочків, необхідних UNIX, навіть \\
.
6. echo
реалізації, які можуть виводити довільні дані дослівно
Строго кажучи, ви також можете порахувати, що FreeBSD / macOS /bin/echo
вгорі (а не echo
вбудована оболонка ), де можна записати zsh
s echo -E - "$var"
або yash
's ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
):
/bin/echo "$var
\c"
Реалізації, які підтримують -E
та -n
(або можуть бути налаштовані), також можуть:
echo -nE "$var
"
І zsh
's echo -nE - "$var"
( printf %s "$var"
) можна було написати
/bin/echo "$var\c"
7. _AST_FEATURES
і АСТUNIVERSE
Це _AST_FEATURES
не призначено для маніпулювання безпосередньо, воно використовується для розповсюдження параметрів конфігурації AST під час виконання команд. Конфігурація має бути виконана за допомогою (недокументованого) astgetconf()
API. Всередині ksh93
, то getconf
вбудований (включено builtin getconf
або виклику command /opt/ast/bin/getconf
) є інтерфейсомastgetconf()
Наприклад, вам слід builtin getconf; getconf UNIVERSE = att
змінити UNIVERSE
налаштування на att
(змусити echo
поводитись між іншим способом SysV). Після цього ви помітите, що $_AST_FEATURES
змінна середовища містить UNIVERSE = att
.
echo -e
?