Вот портативное решение, которое должно работать с большинством, если не со всеми синтаксическими оболочками Bourne; протестировано с bash
, dash
, ksh93
, ksh88
, ash
, (Борном)sh
иzsh
:
for command in "ls -ßnonsense" "ls -ld /tmp"; do
foo=`{ eval "$command"; echo $? > /tmp/ps.$$; } | tee /dev/tty;`
st=`cat /tmp/ps.$$;rm /tmp/ps.$$`
echo "foo=$foo status=$st"
done
Используйте zsh
вместо:
shell_code='ls' # or ls -ßnonsense...
{ foo=$(eval " $shell_code" >&1 >&3 3>&-); } 3>&1
print -r status=$? output=$foo
Здесь,мы дублируем исходный stdout (, который был бы управляющим tty только в том случае, если бы этот скрипт запускался ненаправленно с терминала )на fd 3, и перенаправлял вывод eval
как на подстановку команд, так и на этот fd 3. (использование собственного tee
механизма zsh, когда fd перенаправляется более одного раза для вывода ).
С bash
и другими оболочками, поддерживающими pipefail
(, но не ksh93, если в Linux/Cygwin )в системах с /dev/fd/<n>
, вы можете сделать что-то подобное с:
shell_code='ls' # or ls -ßnonsense...
{
foo=$(
set -o pipefail
eval " $shell_code" 3>&- |
tee 4>&1 >&3 3>&- /dev/fd/4
)
} 3>&1
printf 'status=%s output=%s\n' "$?" "$foo"
Если pipefail
включено, статус завершения конвейера соответствует состоянию крайней правой команды в конвейере, которая завершилась ошибкой. Так что здесь будет так же, как eval
, если только tee
не потерпит неудачу.
Чтобы безоговорочно получить статус выхода eval
, с помощью bash
вы можете выполнить:
shell_code='ls' # or ls -ßnonsense...
{
foo=$(
eval " $shell_code" 3>&- |
tee 4>&1 >&3 3>&- /dev/fd/4
exit "$PIPESTATUS"
)
} 3>&1
printf 'status=%s output=%s\n' "$?" "$foo"
В этих подходах stdout tee
является исходным stdout, в то время как конвейер подстановки команд подается через /dev/fd/4
. Мы делаем это, а не наоборот(tee
's stdout для канала подстановки команд, записывая исходный stdout через некоторый/dev/fd/<n>
)хотя это немного усложняет код, чтобы обойти тот факт, что последний не будет работать в системах Linux или Cygwin, где открытие /dev/fd/<n>
— это не то же самое, что дублирование fd <n>
(, но выполнение этого для fd, указывающего на конец записи канала, например, для этой подстановки команд (not ksh93, который вместо этого использует пары сокетов ), функционально эквивалентен)