¿Cómo puedo ejecutar un comando en bash después de cualquier cambio en $ PWD?

10

zsh proporciona algunas buenas funciones de enlace , incluso chpwdpara ejecutar una función después de que el usuario cambie los directorios.

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

Estoy tratando de emular eso en bash.

Restricciones:

  • Debe funcionar tanto en shells interactivos como no interactivos, lo que creo que significa que no puede confiar en algo como $PROMPT_COMMAND
  • No se puede redefinir cd, porque quiero que funcione para cualquier comando que cambie los directorios (por ejemplo, pushdy popd)
  • Debe ejecutarse después del comando del usuario, por lo trap "my_function" DEBUGque no funciona, a menos que pueda decir de alguna manera, "primero ejecute lo $BASH_COMMANDque atrapamos, luego también haga esto ..." Veo que puedo evitar la ejecución automática de $BASH_COMMAND if si extdebug está habilitado y la función trap devuelve 1, pero no creo que quiera forzar extdebug, y regresar 1para un comando exitoso (pero modificado) parece incorrecto.

La última parte, "ejecutar después del comando del usuario", es lo que actualmente me tiene perplejo. Si puedo ejecutar una función después de cada comando, puedo hacer que verifique si el directorio ha cambiado desde la última vez que lo verificamos. P.ej:

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
}

¿Estoy en el camino correcto, o hay una mejor manera? ¿Cómo puedo ejecutar un comando en bash después de que el usuario cambie los directorios?

Nathan Long
fuente
3
¿Por qué no redefinir cd, pushdy popd? ¿De cuántas otras maneras hay para cambiar el directorio?
jw013
@ jw013 este código está destinado a entrar en un proyecto de código abierto en el que el responsable de mantenimiento ha enumerado específicamente no redefinir cdcomo principio.
Nathan Long
Además, "de cuántas otras maneras hay para cambiar el directorio", no lo sé, lo cual es otra razón por la que preferiría no confiar en enumerarlos explícitamente.
Nathan Long
¿Por qué quieres hacer esto? Su función puede frenar muchos programas (C, Java, ..). En las secuencias de comandos que uso MYBIN=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )No modifique los comandos de confianza de Unix.
Walter A
1
@NathanLong eso es solo para mi sistema operativo . El sistema operativo parece irrelevante aquí. ¿Importa el sistema operativo? Es el shell lo que importa en su pregunta, y parece que está preguntando específicamente bash, lo que funciona más o menos igual en todos los sistemas operativos en los que se ejecuta.
jw013

Respuestas:

2

No hay forma de cumplir con estas limitaciones

Parece que no hay forma de resolver este problema en bash con mis restricciones. En general, las posibles soluciones son:

  • Anulación cd, pushdy popd, de manera que cualquier comando que cambia directorios primero se ejecutará la función de enlace. Pero esto puede crear problemas porque 1) la anulación debe tener cuidado al completar la pestaña como el comando original y devolver el mismo código de salida y 2) si más de una herramienta adopta este enfoque, no pueden jugar bien juntos
  • Anule todos los comandos que se pueden ejecutar con los cambios del entorno para ejecutar primero la función de enlace. Esto es difícil porque hay muchos de esos 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`, no después de 3) solo puede haber una función de depuración, por lo que si otra herramienta usa este enfoque, no pueden jugar bien juntos
  • Redefina $PROMPT_COMMANDpara ejecutar la función de enlace primero. Esto es subóptimo porque no funcionará en shells no interactivos y porque si otra herramienta define el comando de solicitud, no pueden jugar bien juntos.

En resumen, parece que la única gran solución sería si bash proporcionara algo como el chpwd_functionsgancho de zshell , pero parece imposible simularlo correctamente.

Nathan Long
fuente
1

Si al mantenedor no le gusta que cambies la definición de cd, otra opción sería redefinir todos los comandos que usan este entorno en el directorio:

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

Esto sería bastante eficiente porque el enganche PWD y la configuración en el directorio solo se procesarían cuando sea necesario.

En su función check_pwd de ejemplo, podría cambiar:

my_function

a:

my_function "$PWD"

para pasar el nuevo cwd (podría ser más modular, comprobable).

Gregor
fuente
"redefinir todos los comandos que usan este entorno en el directorio" Desafortunadamente, no puedo predecir eso.
Nathan Long
0

Instale herramientas inotify de acuerdo con su distribución.

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

En mi ejemplo, el siguiente comando se reiniciará httpdsi algo cambia (Modificar, Crear, Eliminar) en el directorio especificado.

Ali Pandidan
fuente