Как я могу запустить команду в bash после любого изменения в $ PWD?

10

zsh предоставляет несколько приятных функций ловушки , в том числе chpwdдля запуска функции после того, как пользователь меняет каталоги.

# zsh only
function greet() { echo 'hi'; }
chpwd_functions+=("greet")
cd .. # hi
pushd # hi
popd  # hi

Я пытаюсь подражать этому в bash.

Ограничения:

  • Он должен работать как в интерактивных, так и в неинтерактивных оболочках, что, я думаю, означает, что он не может полагаться на что-то вроде $PROMPT_COMMAND
  • Он не может переопределить cd, потому что я хочу, чтобы он работал для любой команды, которая изменяет каталоги (например, pushdи popd)
  • Он должен запускаться после команды пользователя, поэтому trap "my_function" DEBUGне работает, если я не могу как-то сказать там: «сначала запусти $BASH_COMMANDмы в ловушке, а затем сделай это ...» Я вижу, что могу избежать автоматического запуска, $BASH_COMMAND если extdebug включен и функция trap возвращает 1, но я не думаю, что хочу форсировать extdebug, и возвращение 1для успешной (но измененной) команды кажется неправильным.

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

function check_pwd() {
  # true in a new shell (empty var) or after cd
  if [ "$LAST_CHECKED_DIR" != "$PWD" ]; then
    my_function
  fi
  LAST_CHECKED_DIR=$PWD
}

Я на правильном пути или есть лучший путь? Как я могу запустить команду в bash после того, как пользователь сменит каталоги?

Натан Лонг
источник
3
Почему бы не пересмотреть cd, pushdи popd? Сколько еще способов изменить каталог?
jw013
@ jw013 этот код предназначен для использования в проекте с открытым исходным кодом, в котором разработчик специально перечислил не переопределение cdв качестве принципа.
Натан Лонг
Кроме того - «сколько существует других способов изменить каталог» - я не знаю, что является еще одной причиной, по которой я предпочел бы не полагаться на их явный вывод.
Натан Лонг
почему ты хочешь сделать это? Ваша функция может тормозить много программ (C, Java, ..). В скриптах, которые я использую MYBIN=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P ), не меняйте доверенные команды Unix.
Вальтер,
1
@NathanLong это только для моей операционной системы . Операционная система здесь не имеет значения. Имеет ли значение ОС? Это оболочка, которая имеет значение в вашем вопросе, и вы, кажется, об этом конкретно спрашиваете bash, которая работает практически одинаково на всех операционных системах, на которых она работает.
jw013

Ответы:

2

Там нет способа, который отвечает этим ограничениям

Похоже, что нет способа решить эту проблему в bash с моими ограничениями. В целом, возможные решения:

  • Переопределите cd, pushdи popd, чтобы любая команда, которая изменяет каталоги, сначала запускала функцию ловушки. Но это может создать проблемы, потому что 1) переопределение должно быть осторожным, чтобы завершить табуляцию, как исходная команда, и вернуть тот же код завершения, и 2) если более одного инструмента используют такой подход, они не могут хорошо играть вместе
  • Переопределите все команды, которые могут быть выполнены с изменениями среды, чтобы сначала запустить функцию ловушки. Это сложно, потому что таких команд много
  • trap 'my_functionDEBUG so that every command will run the hook function. This is suboptimal because 1) it runs before every command, 2) it runs *before*cd`, не после 3) может быть только одна функция отладки, поэтому, если другой инструмент использует этот подход, они не могут хорошо играть вместе
  • Переопределите, $PROMPT_COMMANDчтобы сначала запустить функцию ловушки. Это неоптимально, потому что это не будет работать в неинтерактивных оболочках, и потому что, если другой инструмент определяет команду приглашения, они не могут хорошо играть вместе.

Короче говоря, кажется, что единственное отличное решение было бы, если бы bash предоставил что-то вроде хука zshell chpwd_functions, но кажется, что это невозможно смоделировать правильно.

Натан Лонг
источник
1

Если сопровождающему не нравится, когда вы меняете определение для cd, другим вариантом будет переопределение всех команд, которые используют эту среду в каталоге:

for c in cmd1 cmd2 cmd3 cmd4 cmd5 ; do
    eval "$c() { check_pwd ; command $c \"\$@\" ; }"
done

Это было бы весьма эффективно, потому что ловушка PWD и конфигурация в каталоге будут обрабатываться только при необходимости.

В вашем примере функции check_pwd я мог бы изменить:

my_function

чтобы:

my_function "$PWD"

чтобы передать новый cwd (может быть более модульным, тестируемым).

Грегор
источник
«переопределить все команды, которые используют эту директивную среду». К сожалению, я не могу предсказать это.
Натан Лонг
0

Установите inotify-tools в соответствии с вашим дистрибутивом.

inotifywait -emodify,create,delete -m /path/to/directory | while read line; do service httpd reload; done

В моем примере следующая команда будет перезапущена, httpdесли что-то изменится (Изменить, Создать, Удалить) в указанном каталоге.

Али Пандидан
источник