Запуск `exec` со встроенным Bash
Я определил функцию оболочки (давайте назовем ее clock
), который я хочу использовать в качестве оболочки для другой команды, аналогичной time
функция, например clock ls -R
,
Моя функция оболочки выполняет некоторые задачи и затем заканчивается exec "$@"
,
Я хотел бы, чтобы эта функция работала даже со встроенными оболочками, например clock time ls -R
должен вывести результат time
встроенный, а не /usr/bin/time
исполняемый файл. Но exec
всегда заканчивается тем, что запускает команду
Как я могу заставить мою функцию Bash работать как оболочка, которая также принимает встроенные функции оболочки в качестве аргументов?
Изменить: я только что узнал, что time
это не встроенный Bash, а специальное зарезервированное слово, относящееся к конвейерам. Меня все еще интересует решение для встроенных модулей, даже если оно не работает с time
, но более общее решение было бы еще лучше.
4 ответа
Вы определили функцию bash. Таким образом, вы уже находитесь в оболочке bash при вызове этой функции. Таким образом, эта функция может выглядеть так:
clock(){
echo "do something"
$@
}
Эта функция может быть вызвана с помощью встроенных команд bash, специальных зарезервированных слов, команд и других определенных функций:
Псевдоним:
$ clock type ls
do something
ls is aliased to `ls --color=auto'
Встроенный Bash:
$ clock type type
do something
type is a shell builtin
Еще одна функция:
$ clock clock
do something
do something
Исполняемый файл:
$ clock date
do something
Tue Apr 21 14:11:59 CEST 2015
Единственный способ запустить встроенную оболочку или ключевое слово shell - это запустить новую оболочку, потому что exec "заменяет оболочку данной командой". Я думал, что это интересная проблема, но не должна быть слишком сложной для решения. Однако для правильного цитирования потребовался почти час проб и ошибок (Bash отлично, но я не фанат цитирования).
Вы должны заменить свою последнюю строку на:
exec bash -c ""$@""
Это работает как со встроенными, так и зарезервированными словами; принцип тот же.
Если оболочка должна вставить код перед данной командой, псевдоним будет работать, поскольку они раскрываются на очень ранней стадии:
alias clock="do this; do that;"
Псевдонимы почти буквально вставляются вместо псевдонима, поэтому завершающий ;
важно - это делает clock time foo
расширить до do this; do that; time foo
,
Вы можете использовать это, чтобы создать магические псевдонимы, которые даже обходят цитирование.
Для вставки кода после команды вы можете использовать ловушку "DEBUG".
shopt -s extdebug
trap "<code>" DEBUG
В частности:
shopt -s extdebug
trap 'if [[ $BASH_COMMAND == "clock "* ]]; then
eval "${BASH_COMMAND#clock }"; echo "Whee!"; false
fi' DEBUG
Хук по-прежнему работает перед командой, но по мере ее возврата false
он говорит bash отменить выполнение (потому что ловушка уже запустила его через eval).
В качестве другого примера, вы можете использовать это для псевдонима command please
в sudo command
:
trap 'case $BASH_COMMAND in *\ please) eval sudo ${BASH_COMMAND% *}; false; esac' DEBUG
Единственное решение, которое я мог бы найти до сих пор, - это выполнить анализ случая, чтобы определить, является ли первый аргумент командой, встроенным или ключевым словом, и в последнем случае произойдет сбой:
#!/bin/bash
case $(type -t "$1") in
file)
# do stuff
exec "$@"
;;
builtin)
# do stuff
builtin "$@"
;;
keyword)
>&2 echo "error: cannot handle keywords: $1"
exit 1
;;
*)
>&2 echo "error: unknown type: $1"
exit 1
;;
esac
Не справляется time
тем не менее, так что может быть лучшее (и более краткое) решение.