Запуск `exec` со встроенным Bash

9

Я определил функцию оболочки (давайте назовем ее clock), которую я хочу использовать в качестве оболочки для другой команды, аналогично timeфункции, например clock ls -R.

Моя функция оболочки выполняет некоторые задачи и затем заканчивается на exec "$@".

Мне бы хотелось, чтобы эта функция работала даже со встроенными оболочками, например, clock time ls -Rдолжна выводить результат timeвстроенной функции, а не /usr/bin/timeисполняемый файл. Но execвсегда заканчивается тем, что запускает команду.

Как я могу заставить мою функцию Bash работать как оболочка, которая также принимает встроенные функции оболочки в качестве аргументов?

Редактировать : я только что узнал, что timeэто не встроенный Bash, а специальное зарезервированное слово, относящееся к конвейерам. Мне все еще интересно решение для встроенных модулей, даже если оно не работает time, но более общее решение было бы еще лучше.

Anol
источник
Вам нужно явно вызвать оболочку, используя exec bash -c \' "$@" \'. Если ваша команда в первом параметре не будет распознана как сценарий оболочки, она будет интерпретироваться как двоичный файл для непосредственного запуска. В качестве альтернативы, и более просто, просто пропустите execи вызов "@"из вашей оригинальной оболочки.
AFH

Ответы:

9

Вы определили функцию 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
хаос
источник
В этом случае, есть ли разница между выполнением $@и exec $@, если я знаю, что я выполняю фактическую команду?
анол
3
Когда вы используете exec, команда заменяет оболочку. Следовательно, больше нет встроенных элементов, псевдонимов, специальных зарезервированных слов, определенных слов, потому что исполняемый файл выполняется с помощью системного вызова execve(). Этот системный вызов ожидает исполняемый файл.
хаос
Но с точки зрения внешнего наблюдателя, все еще возможно их различить, например, когда exec $0есть один процесс, а $@еще два.
анол
4
Да, $@имеет запущенную оболочку в качестве родителя и команду в качестве дочернего процесса. Но когда вы хотите использовать встроенные функции, псевдонимы и так далее, вы должны сохранить оболочку. Или начать новый.
хаос
4

Единственный способ запустить встроенную оболочку или ключевое слово shell - это запустить новую оболочку, потому что exec «заменяет оболочку данной командой». Вы должны заменить свою последнюю строку на:

IFS=' '; exec bash -c "$*"

Это работает как со встроенными, так и зарезервированными словами; принцип тот же.

Энтони Геогеган
источник
3

Если оболочка должна вставить код перед данной командой, псевдоним будет работать, поскольку они раскрываются на очень ранней стадии:

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
user1686
источник
1

Единственное решение, которое я мог бы найти до сих пор, - это выполнить анализ случая, чтобы определить, является ли первый аргумент командой, встроенным или ключевым словом, и в последнем случае произойдет сбой:

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

Anol
источник