Почему make игнорирует escape-последовательность, если вывод передается по конвейеру?
В Makefile я хочу напечатать байт, представленный в виде шестнадцатеричного числа, и передать его в STDIN другой программы. По какой-то причине это не работает:
without-pipe:
@printf '\x66\x6f\x6f'
with-bash-and-pipe:
@/bin/bash -c "printf '\x66\x6f\x6f' | cat"
with-pipe:
@printf '\x66\x6f\x6f' | cat
Запуск этого файла производит:
$ make without-pipe
foo
$ make with-bash-and-pipe
foo
$ make with-pipe
\x66\x6f\x6f
Какая особенность make
я пропускаю и как правильно заставить последнюю цель произвести тот же результат. Тот самый with-bash-and-pipe
это своего рода обходной путь.
1 ответ
Примечание: мой тестовый стенд - Ubuntu 18.04.2 LTS.
Несколько шагов требуется, чтобы понять поведение.
1. Сделать несколько умнее
Это выглядит как make
определяет, требуется ли оболочка. я сделал
strace -f -e execve make without-pipe
и часть вывода была:
[pid 17526] execve("/usr/local/sbin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = -1 ENOENT (No such file or directory)
[pid 17526] execve("/usr/local/bin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = -1 ENOENT (No such file or directory)
[pid 17526] execve("/usr/sbin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = -1 ENOENT (No such file or directory)
[pid 17526] execve("/usr/bin/printf", ["printf", "\\x66\\x6f\\x6f"], 0x5569b5a5b440 /* 67 vars */) = 0
Так что в этом случае make
просто исследует возможные места printf
(в соответствии с $PATH
) пока инструмент не найден.
Поведение отличается в последнем случае. Эта команда
strace -f -e execve make with-pipe
урожайность (среди других линий)
[pid 17592] execve("/bin/sh", ["/bin/sh", "-c", "printf '\\x66\\x6f\\x6f' | cat"], 0x561465476440 /* 67 vars */) = 0
Инструмент достаточно умен, чтобы сказать, что вы хотите запустить трубу. Если вы делаете, оболочка используется. Оболочка sh
,
2. В трех случаях используются разные printf
реализации
Как показано выше,
make without-pipe
использованияprintf
исполняемый файл доступен в ОС.printf
является встроенным в Bash (подтвердите сtype -a printf
), такmake with-bash-and-pipe
использует встроенный из Bash.printf
также встроенный в sh (подтвердите с(unset PATH; printf 'printf works\n'; ls)
; если бы он не был встроенным, sh не смог бы найти исполняемый файл, как он не может найтиls
), такmake with-pipe
использует встроенный из ш.
3. printf
не требуется, чтобы понять \x66
и тому подобное
POSIX спецификацияprintf
говорит:
Операнд формата должен использоваться как строка формата, описанная в нотации формата файла XBD, со следующими исключениями:
[...]
Ни один из двух связанных документов не говорит \xHH
и такие должны быть особенными. Я не уверен, что спецификация явно позволяет им быть особенными, но на самом деле они особенные для некоторых printf
в вопросе.
Но не для printf
встроенный в ш.
Как правильно заставить последнюю цель производить тот же результат? Тот самый
with-bash-and-pipe
это своего рода обходной путь.
Вы можете убедиться printf
вы запускаете не встроенную, указав полный путь:
/usr/bin/printf '\x66\x6f\x6f' | cat
однако это требует, чтобы вы знали путь заранее. Чтобы исправить эту проблему, вы можете использовать env
:
env printf '\x66\x6f\x6f' | cat
env
запустит исполняемый файл, найденный в $PATH
, Вы можете ожидать env
присутствовать, потому что это требуется POSIX.
С другой стороны, в общем, вы не можете быть уверены, что любой printf
исполняемый (встроенный или нет) поддерживает \xHH
на первом месте. Поэтому реальное исправление состоит в том, чтобы переписать вашу строку формата так, чтобы она правильно понималась всеми POSIX-совместимыми реализациями printf
, Это кажется полезным:
В дополнение к escape-последовательностям, показанным в обозначении формата файла XBD (
\\
,\a
,\b
,\f
,\n
,\r
,\t
,\v
),\ddd
где ddd представляет собой однозначное, двухзначное или трехзначное восьмеричное число, записывается в виде байта с числовым значением, указанным восьмеричным числом.
Пример:
printf '\146\157\157'
Этот подход работает во всех трех случаях.