Запуск `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 тем не менее, так что может быть лучшее (и более краткое) решение.

Другие вопросы по тегам