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


9

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

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

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

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

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


Вам нужно явно вызвать оболочку, используя 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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.