Làm cách nào tôi có thể chạy một lệnh trong bash sau khi có bất kỳ thay đổi nào trong $ PWD?


10

zsh cung cấp một số hàm hook đẹp , bao gồm chpwdđể chạy một hàm sau khi người dùng thay đổi thư mục.

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

Tôi đang cố gắng mô phỏng điều đó trong bash.

Các ràng buộc:

  • Nó phải hoạt động trong cả vỏ tương tác và không tương tác, mà tôi nghĩ có nghĩa là nó không thể dựa vào thứ gì đó như $PROMPT_COMMAND
  • Nó không thể xác định lại cd, vì tôi muốn nó hoạt động cho bất kỳ lệnh nào thay đổi thư mục (ví dụ: pushdpopd)
  • Nó phải chạy theo lệnh của người dùng, vì vậy trap "my_function" DEBUGkhông hoạt động, trừ khi tôi có thể nói bằng cách nào đó, "trước tiên hãy chạy $BASH_COMMANDchúng tôi bị mắc kẹt, sau đó cũng làm điều này ..." Tôi thấy rằng tôi có thể tránh tự động chạy $BASH_COMMAND nếu extdebug được bật hàm bẫy trả về 1, nhưng tôi không nghĩ rằng tôi muốn ép buộc extdebugvà quay lại 1lệnh thành công (nhưng đã sửa đổi) có vẻ sai.

Phần cuối cùng - "chạy theo lệnh của người dùng" - là phần hiện tại khiến tôi bối rối. Nếu tôi có thể chạy một chức năng sau mỗi lệnh, tôi có thể kiểm tra xem thư mục đã thay đổi kể từ lần kiểm tra cuối cùng chưa. Ví dụ:

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
}

Tôi đang đi đúng hướng, hay có cách nào tốt hơn? Làm thế nào tôi có thể chạy một lệnh trong bash sau khi người dùng thay đổi thư mục?


3
Tại sao không xác định lại cd, pushdpopd? Có bao nhiêu cách khác để thay đổi thư mục?
jw013

@ jw013 mã này được dự định đi vào một dự án nguồn mở trong đó người bảo trì đã liệt kê cụ thể không xác định lại cdtheo nguyên tắc.
Nathan Long

Ngoài ra - "có bao nhiêu cách khác để thay đổi thư mục" - Tôi không biết, đó là một lý do khác mà tôi không muốn dựa vào việc liệt kê chúng một cách rõ ràng.
Nathan Long

tại sao bạn muốn làm việc này? Chức năng của bạn có thể hãm rất nhiều chương trình (C, Java, ..). Trong các tập lệnh tôi sử dụng MYBIN=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )Xin vui lòng không thay đổi các lệnh Unix đáng tin cậy.
Walter A

1
@NathanLong chỉ dành cho hệ điều hành của tôi . Hệ điều hành dường như không liên quan ở đây. Hệ điều hành có vấn đề? Đó là cái vỏ quan trọng trong câu hỏi của bạn và dường như bạn đang hỏi cụ thể bash, cái này hoạt động khá giống nhau trên tất cả các hệ điều hành mà nó chạy.
jw013

Câu trả lời:


2

Không có cách nào đáp ứng những hạn chế này

Có vẻ như không có cách nào để giải quyết vấn đề này trong bash với các ràng buộc của tôi. Nói chung, các giải pháp có thể là:

  • Ghi đè cd, pushdpopd, để bất kỳ lệnh nào thay đổi thư mục trước tiên sẽ chạy chức năng hook. Nhưng điều này có thể tạo ra vấn đề vì 1) ghi đè phải cẩn thận để hoàn thành tab như lệnh ban đầu và trả lại cùng một mã thoát và 2) nếu có nhiều hơn một công cụ thực hiện phương pháp này, chúng không thể chơi tốt với nhau
  • Ghi đè tất cả các lệnh có thể được chạy với các thay đổi môi trường để trước tiên chạy chức năng hook. Điều này thật khó vì có nhiều lệnh như vậy
  • 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`, không phải sau 3) chỉ có thể có một chức năng gỡ lỗi, vì vậy nếu một công cụ khác sử dụng phương pháp này, chúng không thể chơi tốt với nhau
  • Xác định lại $PROMPT_COMMANDđể chạy chức năng hook đầu tiên. Điều này là tối ưu vì nó sẽ không hoạt động trong các shell không tương tác và bởi vì nếu một công cụ khác xác định lệnh nhắc nhở, chúng không thể chơi tốt với nhau.

Nói tóm lại, có vẻ như giải pháp tuyệt vời duy nhất là nếu bash cung cấp thứ gì đó giống như chpwd_functionscái móc của zshell , nhưng dường như không thể mô phỏng đúng cách.


1

Nếu nhà bảo trì không thích bạn thay đổi định nghĩa cho cd, một tùy chọn khác sẽ là xác định lại tất cả các lệnh sử dụng môi trường trong thư mục này:

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

Điều này sẽ khá hiệu quả vì hook PWD và cấu hình trong thư mục sẽ chỉ được xử lý khi có yêu cầu.

Trong ví dụ của bạn hàm check_pwd, tôi có thể thay đổi:

my_function

đến:

my_function "$PWD"

để vượt qua trong cwd mới (có thể là mô-đun nhiều hơn, có thể kiểm tra).


"xác định lại tất cả các lệnh sử dụng môi trường trong thư mục này" Thật không may, tôi không thể dự đoán điều đó.
Nathan Long

0

Cài đặt công cụ inotify theo phân phối của bạn.

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

Trong ví dụ của tôi, lệnh sau sẽ khởi động lại httpdnếu có bất cứ điều gì thay đổi (Sửa đổi, Tạo, Xóa) trong thư mục được chỉ định.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.