Como posso executar um comando no bash após qualquer alteração no $ PWD?

10

O zsh fornece algumas boas funções de gancho , incluindo chpwda execução de uma função depois que o usuário altera os diretórios.

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

Estou tentando imitar isso no bash.

Restrições:

  • Ele deve funcionar em shells interativos e não interativos, o que eu acho que significa que ele não pode confiar em algo como $PROMPT_COMMAND
  • Não pode redefinir cd, porque quero que funcione para qualquer comando que altere diretórios (por exemplo, pushde popd)
  • Ele deve ser executado após o comando do usuário, para trap "my_function" DEBUGque não funcione, a menos que eu possa de alguma forma dizer lá, "primeiro execute o $BASH_COMMANDtrapping, depois faça isso ..." Vejo que posso evitar a execução automática de $BASH_COMMAND se extdebug estiver ativado ea função trap retorna 1, mas acho que não quero forçar extdebug, e retornar 1para um comando bem-sucedido (mas modificado) parece errado.

A última parte - "execute o comando do usuário" - é o que atualmente me deixou perplexo. Se eu puder executar uma função após cada comando, posso verificar se o diretório foi alterado desde a última verificação. Por exemplo:

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
}

Estou no caminho certo ou existe uma maneira melhor? Como posso executar um comando no bash depois que o usuário altera os diretórios?

Nathan Long
fonte
3
Por que não redefinir cd, pushde popd? Quantas outras maneiras existem para alterar o diretório?
Jk013
@ jw013 este código destina-se a um projeto de código aberto no qual o mantenedor listou especificamente não redefinir cdcomo princípio.
Nathan Long
Além disso - "quantas outras maneiras existem para alterar o diretório" - não sei, que é outra razão pela qual prefiro não confiar em listá-las explicitamente.
Nathan Long
Por que você quer fazer isso? Sua função pode interromper muitos programas (C, Java, ..). Nos scripts que eu uso, por MYBIN=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )favor, não altere os comandos Unix confiáveis.
Walter A
1
@ NathanLong isso é apenas para o meu sistema operacional . O sistema operacional parece irrelevante aqui. O SO importa? É o shell que importa na sua pergunta, e você parece estar perguntando especificamente bash, o que funciona da mesma forma em todos os sistemas operacionais em que é executado.
Jw013

Respostas:

2

Não há como atender a essas restrições

Parece que não há como resolver esse problema no bash com minhas restrições. Em geral, as soluções possíveis são:

  • Override cd, pushde popd, de modo que qualquer comando que altera diretórios irá primeiro executar a função de gancho. Mas isso pode criar problemas, porque 1) a substituição deve ser cuidadosa para concluir a tabulação como o comando original e retornar o mesmo código de saída e 2) se mais de uma ferramenta adotar essa abordagem, elas não poderão funcionar bem juntas
  • Substitua todos os comandos que podem ser executados com as alterações do ambiente para executar primeiro a função de gancho. Isso é difícil porque existem muitos desses comandos
  • 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`, não depois de 3), pode haver apenas uma função de depuração; portanto, se outra ferramenta usar essa abordagem, eles não poderão tocar bem juntos
  • Redefina $PROMPT_COMMANDpara executar a função de gancho primeiro. Isso é subótimo porque não funcionará em shells não interativos e, se outra ferramenta definir o comando prompt, eles não poderão funcionar bem juntos.

Em resumo, parece que a única ótima solução seria se o bash fornecesse algo como o chpwd_functionsgancho do zshell , mas parece impossível simular isso adequadamente.

Nathan Long
fonte
1

Se o mantenedor não gostar de alterar a definição do cd, outra opção seria redefinir todos os comandos que usam este ambiente no diretório:

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

Isso seria bastante eficiente porque o gancho PWD e a configuração no diretório seriam processados ​​apenas quando necessário.

No seu exemplo de função check_pwd, posso mudar:

my_function

para:

my_function "$PWD"

para passar no novo cwd (pode ser mais modular, testável).

Gregor
fonte
"redefina todos os comandos que usam esse ambiente no diretório" Infelizmente, não posso prever isso.
Nathan Long
0

Instale as ferramentas inotify de acordo com sua distribuição.

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

No meu exemplo, o comando a seguir será reiniciado httpdse algo mudar (Modificar, Criar, Excluir) no diretório especificado.

Ali Pandidan
fonte