Я хочу быть в состоянии захватить точный вывод подстановки команды, включая завершающие символы новой строки .
Я понимаю, что они удалены по умолчанию, поэтому для их сохранения могут потребоваться некоторые манипуляции, и я хочу сохранить исходный код выхода .
Например, дана команда с переменным числом завершающих строк новой строки и кодом выхода:
f(){ for i in $(seq "$((RANDOM % 3))"); do echo; done; return $((RANDOM % 256));}
export -f f
Я хочу запустить что-то вроде:
exact_output f
И иметь вывод:
Output: $'\n\n'
Exit: 5
Я заинтересован в обоих bash
и POSIX sh
.
$IFS
, поэтому она не будет использоваться в качестве аргумента.IFS
(попробуйте( IFS=:; subst=$(printf 'x\n\n\n'); printf '%s' "$subst" )
. Только символы новой строки удаляются.\t
И `` не делают, иIFS
не влияют на это.tcsh
Ответы:
POSIX снаряды
Обычный ( 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ) трюк для получения полного вывода команды состоит в следующем:
Идея состоит в том, чтобы добавить и доп
.\n
. Подстановка команд только лишит этого\n
. И ты раздеваешь.
с${output%.}
.Обратите внимание, что в других оболочках это не
zsh
будет работать, если выходные данные имеют байты NUL. Сyash
этим не сработает, если вывод не текстовый.Также обратите внимание, что в некоторых локалях важно, какой символ вы используете для вставки в конце.
.
как правило, должно быть хорошо, но некоторые другие не могут. Напримерx
(как используется в некоторых других ответах) или@
не будет работать в локали, использующей кодировки BIG5, GB18030 или BIG5HKSCS. В этих кодировках кодировка ряда символов заканчивается тем же байтом, что и кодировкаx
или@
(0x78, 0x40)Например,
ū
в BIG5HKSCS 0x88 0x78 (иx
0x78, как в ASCII, все кодировки в системе должны иметь одинаковую кодировку для всех символов переносимого набора символов, который включает английские буквы,@
и.
). Так что, еслиcmd
былоprintf '\x88'
и мы вставилиx
после него,${output%x}
будет не в состоянии раздеться , что ,x
как$output
бы на самом деле содержатū
.Использование
.
вместо этого может привести к той же проблеме в теории, если есть какие-либо символы, кодировка которых заканчивается в той же кодировке, что и.
, но для проверки некоторое время назад, я могу сказать, что ни одна из кодировок, которые могут быть доступны для использования в локали в в системах Debian, FreeBSD или Solaris есть такие символы, которые мне достаточно хороши (и почему я остановился на том,.
что также является символом, обозначающим конец предложения на английском языке, поэтому кажется уместным).Более правильный подход, как обсуждал @Arrow, состоял бы в том, чтобы изменить языковой стандарт на C только для удаления последнего символа (
${output%.}
), который обеспечил бы удаление только одного байта, но это значительно усложнило бы код и потенциально привело бы к проблемам совместимости свой.альтернативы bash / zsh
С
bash
иzsh
, предполагая, что вывод не имеет NUL, вы также можете сделать:Для того, чтобы получить статус выхода
cmd
, вы можете сделатьwait "$!"; ret=$?
в ,bash
но не вzsh
.гс / эс / akanaga
Для полноты, заметим , что
rc
/es
/akanga
есть оператор для этого. В них подстановка команд, выраженная как`cmd
(или`{cmd}
для более сложных команд), возвращает список (путем разделения на$ifs
space-tab-newline по умолчанию). В этих оболочках (в отличие от оболочек типа Борна) удаление новой строки выполняется только как часть этого$ifs
разделения. Таким образом, вы можете либо очистить,$ifs
либо использовать``(seps){cmd}
форму, в которой вы указываете разделители:или:
В любом случае статус выхода команды теряется. Вы должны были бы вставить это в вывод и извлечь его впоследствии, который стал бы уродливым.
рыбы
В рыбе подстановка команд выполняется с
(cmd)
использованием необолочки.Создает
$var
массив со всеми строками в выходных данныхcmd
if, не$IFS
является пустым, или с выводомcmd
разделенных до одного (в отличие от всех в большинстве других оболочек) символа новой строки, если$IFS
он пуст.Так что есть еще проблема в этом
(printf 'a\nb')
и(printf 'a\nb\n')
расширяться до того же, даже с пустым$IFS
.Чтобы обойти это, лучшее, что я мог придумать, было:
Альтернатива состоит в том, чтобы сделать:
Оболочка Борна
Оболочка Bourne не поддерживала
$(...)
ни форму, ни${var%pattern}
оператора, поэтому ее может быть довольно сложно достичь. Один из подходов заключается в использовании eval и цитирования:Здесь мы генерируем
быть переданным
eval
. Что касается подхода POSIX, если'
был один из тех характеров, кодирование может быть найдена в конце других персонажей, мы должны были бы проблемы (гораздо хуже , так как он станет инъекцией команды уязвимостью), но , к счастью, как.
, это не одна из тех, и эта техника цитирования, как правило, используется любой, которая заключает в кавычки шелл-код (обратите внимание, что\
есть проблема, поэтому ее не следует использовать (кроме тех случаев,"..."
внутри которых необходимо использовать обратную косую черту для некоторых символов) Здесь мы используем его только после того, как'
все в порядке).Tcsh
Смотрите, что tcsh сохраняет новые строки в подстановке команд `...`
(без учета состояния выхода, к которому можно обратиться, сохранив его во временном файле (
echo $status > $tempfile:q
после команды))источник
zsh
можете хранитьNUL
в переменной, почему бы неIFS= read -rd '' output < <(cmd)
работать? Он должен иметь возможность хранить длину строки ... она кодируется''
как 1-байтовая строка,\0
а не 0-байтовая строка?read -d ''
рассматривается какread -d $'\0'
(bash
хотя,$'\0'
как и''
везде).x
если он был добавлен. Пожалуйста, посмотрите на мой отредактированный ответ.var=value command eval
уловка обсуждалась здесь ( также ) и в списке рассылки Austin-Group ранее. Вы обнаружите, что он не переносимый (и когда вы пытаетесь сделать что-то вроде тогоa=1 command eval 'unset a; a=2'
или хуже, совершенно очевидно, что он не предназначен для такого использования). То же самое для того,savedVAR=$VAR;...;VAR=$savedVAR
что не делает то, что вы хотите, когда$VAR
был изначально не установлен. Если это обойти только теоретическую проблему (ошибка, которую невозможно устранить на практике), IMO, это не стоит беспокоиться. Тем не менее, я поддержу вас за попытку.LANG=C
для удаления байта из строки? Вы поднимаете проблемы вокруг реальной точки зрения, все легко решить. (1) не используется unset (2) Проверьте переменную перед ее изменением. @ StéphaneChazelasДля нового вопроса этот скрипт работает:
По исполнению:
Длинное описание
Обычная мудрость для оболочек POSIX для удаления
\n
:Это необходимо, потому что последняя новая строка ( S ) удаляется расширением команды согласно спецификации POSIX :
О трейлинге
x
.В этом вопросе было сказано, что
x
можно было бы перепутать с последним байтом некоторого символа в некоторой кодировке. Но как мы собираемся угадать, какой или какой символ лучше в каком-либо языке в некоторой возможной кодировке, это, по меньшей мере, трудное предложение.Тем не мение; Это просто неверно .
Единственное правило, которому мы должны следовать, это добавлять именно то , что мы удаляем.
Должно быть легко понять, что, если мы добавляем что-то к существующей строке (или последовательности байтов), а затем удаляем точно то же самое, исходная строка (или последовательность байтов) должна быть такой же.
Куда мы пойдем не так? Когда мы смешиваем символы и байты .
Если мы добавляем байт, мы должны удалить байт, если мы добавляем символ, мы должны удалить точно такой же символ .
Второй вариант, добавление символа (а затем удаление точно такого же символа) может стать запутанным и сложным, и, да, кодовые страницы и кодировки могут помешать.
Однако первый вариант вполне возможен, и, после его объяснения, он станет простым.
Давайте добавим байт, байт ASCII (<127), и, чтобы сделать вещи как можно менее запутанными, скажем, символ ASCII в диапазоне az. Или, как мы должны сказать, байт в шестнадцатеричном диапазоне
0x61
-0x7a
. Позволяет выбрать любой из них, возможно, х (на самом деле это байт значения0x78
). Мы можем добавить такой байт с помощью конкатенации x к строке (предположим, чтоé
):Если мы посмотрим на строку как последовательность байтов, мы увидим:
Последовательность строк, которая заканчивается на х.
Если мы удалим это x (значение байта
0x78
), мы получим:Работает без проблем.
Немного более сложный пример.
Допустим, интересующая нас строка заканчивается байтом
0xc3
:И давайте добавим байт значения
0xa9
Строка теперь стала такой:
Именно то, что я хотел, последние два байта - это один символ в utf8 (так что любой мог воспроизвести этот результат в своей консоли utf8).
Если мы удалим символ, исходная строка будет изменена. Но это не то, что мы добавили, мы добавили байтовое значение, которое в любом случае записывается как x, а как байт.
Что нам нужно, чтобы избежать неправильной интерпретации байтов как символов. Нам нужно действие, которое удаляет использованный нами байт
0xa9
. Фактически, ash, bash, lksh и mksh, похоже, делают именно это:Но не кш или зш.
Однако это очень легко решить, давайте скажем всем этим оболочкам выполнить удаление байтов:
вот и все, все снаряды протестировали работу (кроме yash) (для последней части строки):
Просто скажи оболочке удалить символ LC_ALL = C, который является ровно одним байтом для всех байтовых значений от
0x00
to0xff
.Решение для комментариев:
Для примера, обсуждаемого в комментариях, одно из возможных решений (которое не работает в zsh):
Это устранит проблему кодирования.
источник
zsh
добавленprintf -v
для совместимостиbash
в декабре 2015 года${var%?}
всегда обрезать один байт, теоретически более корректно, но: 1 -LC_ALL
иLC_CTYPE
переопределение$LANG
, поэтому вам нужно установитьLC_ALL=C
2 - вы не можете сделать этоvar=${var%?}
в подоболочке, как изменение быть потерянным, поэтому вам нужно будет сохранить и восстановить значение и состояниеLC_ALL
(или прибегнуть кlocal
функциям, не относящимся к области POSIX ). 3 - изменение локали в середине сценария не полностью поддерживается в некоторых оболочках, таких как yash. С другой стороны, на практике.
никогда не бывает проблем в реальных кодировках, поэтому его использование позволяет избежать смешения с LC_ALL.Вы можете вывести символ после обычного вывода и затем удалить его:
Это POSIX-совместимое решение.
источник