8

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

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

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

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

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

4 ответа4

8

Вы определили функцию 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
3

Единственный способ запустить встроенную оболочку или ключевое слово shell - это запустить новую оболочку, потому что exec «заменяет оболочку данной командой». Я думал, что это интересная проблема, но не должна быть слишком сложной для решения. Однако для правильного цитирования потребовался почти час проб и ошибок (Bash отлично, но я не фанат цитирования).

Вы должны заменить свою последнюю строку на:

exec bash -c ""$@""

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

2

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

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
0

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .