TIP #24: GIẢI MÃ BIẾN $PATH & HASH TABLE

05/05/2020

BIẾN MÔI TRƯỜNG $PATH

Hệ thống Linux có rất nhiều thư mục, vậy khi ta gõ 1 lệnh trên terminal như: ping, echo, cat, passwd … thì hệ thống sẽ xử lý thế nào để tìm đúng file cần thực thi? Đó là nhờ vào sự hỗ trợ của biến môi trường (environment variable) $PATH. Biến $PATH sẽ chứa danh sách các thư mục mà shell sẽ file thực thi có tên giống như lệnh ta đã gõ vào. Danh sách các thư mục  khai báo trong biến PATH sẽ được cách nhau bởi dấu “:”, ta có thể in biến $PATH bằng lệnh sau:

[[email protected] tmp]$ echo $PATH
/usr/local/openresty/bin:/usr/local/openresty/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

Khi ta nhập một lệnh trên terminal nhưng không sử dụng đường dẫn tuyệt đối, shell sẽ tìm kiếm trong danh sách thư mục trên cho đến khi tìm được file (có quyền thực thi) có tên giống như lệnh ta đã gõ. Nếu đã tìm đến cuối danh sách nhưng vẫn không tìm thấy, shell sẽ trả về lỗi “command not found” hoặc “no such file or directory”

Ví dụ:

  • Tôi tạo một file tên “test_path.sh” có chức năng in ra vị trí hiện tại của file và cấp quyền thực thi (executable) cho nó:
#!/bin/bash

echo ${BASH_SOURCE[0]}
  • Biến $PATH của tôi hiện tại như sau:
[[email protected] tmp]$ echo $PATH
/usr/local/openresty/bin:/usr/local/openresty/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
  • Tôi đặt file trên vào thư mục “/usr/bin” (thư mục có trong danh sách của biến $PATH), như vậy tôi có thể đứng ở bất kỳ đâu và gõ lệnh “test_path.sh”, kết quả nhận được sẽ như sau:
# Chuyển vào thư mục /tmp
[[email protected] ~]$ cd /tmp/

# Thực thi lệnh "test_path.sh"
[[email protected] tmp]$ test_path.sh 
/usr/bin/test_path.sh

HASH TABLE

Tuy nhiên, chắc chắn trong quá trình làm Sysadmin Linux, bạn sẽ gặp trường hợp file cần thực thi rõ ràng nằm trong đường dẫn hợp lệ của $PATH nhưng khi gõ lệnh lại luôn báo “no such file or directory”/“command not found” hoặc thực thi không đúng file mà ta mong muốn. Điều này thường xảy ra khi bạn cài lại hoặc di chuyển file thực thi (thường gặp nhất là python, php, perl, ruby …).

Ví dụ như sau:

  • Vẫn như cũ, file “test_path.sh” tôi đặt tại “/usr/bin”, giờ tôi sẽ move file đó qua thự mục “/usr/local/bin” (cũng là đường dẫn hợp lệ đã được khai báo trong biến $PATH, thâm chí nằm trước cả “/usr/bin”). Kết quả khi thực thi lệnh “test_path.sh”
# Khi file còn nằm ở "/usr/bin"
[[email protected] tmp]$ test_path.sh 
/usr/bin/test_path.sh

# Di chuyển qua qua "/usr/local/bin"
[[email protected] tmp]$ sudo mv /usr/bin/test_path.sh /usr/local/bin/test_path.sh

# Thực thi lại lệnh và gặp lỗi
[[email protected] tmp]$ test_path.sh 
bash: /usr/bin/test_path.sh: No such file or directory

Hệ thống báo lỗi không tìm thấy file, mặc dù file đang nằm trong “/usr/local/bin” và đã được khai báo trong biến $PATH.

Nguyên nhân là do “hash table”: một tính năng của bash giúp hệ thống không phải search lại biến $PATH mỗi khi thực thi lệnh, hash table sẽ lưu lại kết quả tìm kiếm của lần trước vào memory. Do đó, kết quả trong hash table có độ ưu tiên cao hơn biến $PATH. Để in ra hash table hiện tại, dùng lệnh “hash”

[[email protected] tmp]$ hash
hits	command
   5	/usr/bin/test_path.sh
   3	/usr/bin/sudo

Kết quả trên cho thấy “test_path.sh” đang cache lại trên memory ở đường dẫn “/usr/bin”, đó là lý do vì sao khi ta move file “test_path.sh” qua “/usr/local/bin” thì thực thi lệnh “test_path.sh” sẽ báo lỗi không tìm thấy file.

Để giải quyết, ta cần xóa cache của hash table, có 2 cách:

  • Update biến $PATH: Khi biến PATH được update thì hash table sẽ tự động được xóa
  • Sử dụng lệnh “hash” với option “-r”. Tôi sẽ dùng cách này
# Xóa hash table
[[email protected] tmp]$ hash -r

# Kiểm tra lại nội dung hash table
[[email protected] tmp]$ hash
hash: hash table empty

# Thực thi lại "test_path.sh"
[[email protected] tmp]$ test_path.sh 
/usr/local/bin/test_path.sh

# Kiểm tra lại hash table
[[email protected] tmp]$ hash
hits	command
   1	/usr/local/bin/test_path.sh

Chạy lại lệnh “test_path.sh”, lúc này do hash table rỗng, hệ thống sẽ search “test_path.sh” trong các thư mục ở biến $PATH, sau đó cache lại kết quả tìm được vào hash table.

TỔNG KẾT

Kiến thức này có vẻ rất đơn giản và dễ hiểu, nhưng trên thực tế không biết bao nhiêu lần các đồng nghiệp của tôi đã phải tốn rất nhiều thời gian để debug lỗi liên quan đường dẫn khi họ chưa biết sự hiện diện của “hash table” và tính năng cache đường dẫn của nó. Nếu bạn có gặp khó khăn về đường dẫn khi thực thi lệnh, hãy kiểm tra hash table nhé!

Và đừng quên tham gia vào Group Chat Telegram của nhóm để thường xuyên được cập nhật và chia sẻ các kiến thức thú vị khác dành cho Sysadmin Linux!