эхо по сравнению с <<<, или Бесполезное Использование эха в Премии Bash?

От Goosfrabaa в Дуге форумы Linux:

setxkbmap -v | awk -F "+" '/symbols/ {print $2}'

Это работает правильно здесь, и печать us(dvorak-intl) (отображенный как "США Dvorak, международный" в меню клавиатурного набора GNOME).

19
23.06.2016, 15:40
3 ответа

Во-первых, давайте сконцентрируемся на производительности. Я выполнил сравнительные тесты для немного отличающейся программы на иначе главным образом неактивном x86_64 выполнении процессора, которое сжимают Debian.

herestring.bash, использование herestring для передачи строки входа:

#! /bin/bash
i=0
while [ $i -lt $1 ]; do
  tr a-z A-Z <<<'hello world'
  i=$((i+1))
done >/dev/null

heredoc.bash, использование heredoc для передачи строки входа:

#! /bin/bash
i=0
while [ $i -lt $1 ]; do
  tr a-z A-Z <<'EOF'
hello world
EOF
  i=$((i+1))
done >/dev/null

echo.bash, использование echo и канал для передачи строки входа:

#! /bin/bash
i=0
while [ $i -lt $1 ]; do
  echo 'hello world' | tr a-z A-Z
  i=$((i+1))
done >/dev/null

Для сравнения я также синхронизировал сценарии под ATT ksh93 и под тире (за исключением herestring.bash, потому что тире не имеет herestrings).

Вот median-three времена:

$ time bash ./herestring.bash 10000
./herestring.bash 10000  0.32s user 0.79s system 15% cpu 7.088 total
$ time ksh ./herestring.bash 10000
ksh ./herestring.bash 10000  0.54s user 0.41s system 17% cpu 5.277 total
$ time bash ./heredoc.bash 10000
./heredoc.bash 10000  0.35s user 0.75s system 17% cpu 6.406 total
$ time ksh ./heredoc.bash 10000  
ksh ./heredoc.sh 10000  0.54s user 0.44s system 19% cpu 4.925 total
$ time sh ./heredoc.bash 10000  
./heredoc.sh 10000  0.08s user 0.58s system 12% cpu 5.313 total
$ time bash ./echo.bash 10000
./echo.bash 10000  0.36s user 1.40s system 20% cpu 8.641 total
$ time ksh ./echo.bash 10000
ksh ./echo.sh 10000  0.47s user 1.51s system 28% cpu 6.918 total
$ time sh ./echo.sh 10000
./echo.sh 10000  0.07s user 1.00s system 16% cpu 6.463 total

Заключения:

  • heredoc быстрее, чем herestring.
  • echo и канал заметно, но не существенно быстрее. (Следует иметь в виду, что это - игрушечная программа: в реальной программе большая часть времени обработки была бы во что tr звоните обозначает здесь.)
  • Если Вы хотите скорость, удар канавы и тире вызова или еще лучше ksh вместо этого. Функции Bash не восполняют его относительное замедление, но ksh имеет обе функции и скорость.

Вне производительности существует также ясность и мобильность. <<< ksh93/bash/zsh расширение, которое менее известно, чем echo … | или <<. Это не работает в ksh88/pdksh или в POSIX sh.

Единственное место, где <<< возможно значительно более ясно, в heredoc:

foo=$(tr a-z A-Z <<<'hello world')

по сравнению с

foo=$(tr a-z A-Z <<'EOF'
hello world
EOF
)

(Большинство оболочек не может справиться с закрытием круглой скобки в конце строки, содержащей <<EOF.)

11
27.01.2020, 19:45
  • 1
    Отметьте это <<< прибывает из rc, и был сначала принесен к подобному Границе миру оболочки zsh. rc не добавила запаздывающая новая строка, но весь из bash, zsh и ksh93 сделайте AFAICT. rc использует канал там, в то время как bash, zsh и ksh93 используйте временный файл как для heredocs. –  Stéphane Chazelas 21.12.2012, 04:14
  • 2
    @StephaneChazelas ой, я должен был проверить, спасибо. Это делает <<< еще менее полезный. –  Gilles 'SO- stop being evil' 21.12.2012, 04:35
  • 3
    Также отметьте это zsh имеет оптимизацию в =(<<<foo) эффективно создать временный файл, который содержит "нечто", которое не включает разветвление процесса как =(echo foo) был бы. –  Stéphane Chazelas 21.12.2012, 04:40
  • 4
    я также упомяну, что pdksh не включает <<<. –  kurtm 15.10.2013, 17:54
  • 5
    @kurtm “Это не работает в ksh88/pdksh или в POSIX sh”. –  Gilles 'SO- stop being evil' 15.10.2013, 18:19

Другая причина использовать heredocs (если у Вас не было достаточно) состоит в том, что эхо может перестать работать, если поток не используется. Рассмотрите удар наличия' pipefail опция:

set -o pipefail
foo=yawn
echo $foo | /bin/true ; echo $?  # returns 0

/bin/true не использует его вход стандарта, но echo yawn завершается, тем не менее. Однако, если эхо попросят распечатать много данных, то оно не завершится до окончания true завершился:

foo=$(cat /etc/passwd)
# foo now has a fair amount of data

echo $foo | /bin/true ; echo $?  # returns 0 sometimes 141
echo $foo$foo$foo$foo | /bin/true ; echo $?  # returns mostly 141

141 SIGPIPE (128 + 13) (128 добавляемый, потому что удар делает так согласно удару (1):

Когда команда завершается на фатальном сигнале N, удар использует значение 128+N как статус выхода.

Heredocs не имеют этой проблемы:

/bin/true <<< $foo$foo$foo$foo ; echo $?  # returns 0 always
6
27.01.2020, 19:45
  • 1
    Спасибо, этот конкретный нюанс заставлял мой сценарий приводить к сбою периодически наугад места, больше на серверах Amazon, чем на хостах kvm, но он все еще время от времени перестал работать в на вид невозможных к сбою местах. Это - хорошая вещь что pipefail значения по умолчанию к тому, чтобы быть прочь! –  mogsie 15.10.2013, 16:29
  • 2
    О, я включаю pipefail наверху каждого сценария. Дай мне все ошибки! –  l0b0 15.10.2013, 18:48
  • 3
    @l0b0 Хм. Возможно, я добавлю pipefail к j.mp/safebash, я - все для получения всех ошибок во время разработки! –  Bruno Bronosky 22.01.2014, 04:15

Одна из причин, по которой вы можете захотеть использовать эхо, состоит в том, чтобы обеспечить некоторый контроль над символом новой строки, который добавляется в конец heredocs и herestrings:

Три символа fooимеют длина 3:

$ echo -n foo | wc -c
3

Тем не менее, трехсимвольная строка здесь состоит из четырех символов:

$ wc -c <<< foo
4

Трехсимвольная строка здесь тоже:

$ wc -c << EOF
foo
EOF
4

Четвертый символ — символ новой строки 0x0a.

Это каким-то волшебным образом согласуется с тем, как bash удаляет эти символы новой строки при захвате вывода из подоболочки:

Вот команда, которая возвращает четыре символа: fooи \n . '\n' добавляется с помощью echo, он всегда добавляет символ новой строки, если только вы не укажете опцию -n:

$ echo foo
foo
$ echo foo | wc -c
4

Однако, присваивая это переменной, завершающая новая строка, добавленная с помощью echo, удаляется. :

$ foo=$(echo foo)
$ echo "$foo" # here, echo adds a newline too.
foo

Итак, если вы смешиваете файлы и переменные и используете их в вычислениях (например, вы не можете использовать heredocs или herestrings, так как они добавят новую строку.

foo=abc
echo -n 'abc' > something.txt
if [ $(wc -c <<< "$foo") -eq $(wc -c < something.txt) ] ; then
  echo "yeah, they're the same!"
else
  echo "foo and bar have different lengths (except, maybe not)"
fi

Если вы измените оператор if на чтение

if [ $(echo -n "$foo" | wc -c) -eq $(wc -c < something.txt) ] ; then

, тогда тест проходит.

3
27.01.2020, 19:45

Теги

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