Mục đích của lệnh băm là gì?


118

Nếu bạn chạy, hashnó hiển thị đường dẫn của tất cả các lệnh chạy vì hàm băm được đặt lại lần cuối ( hash -r)

[root@c04c ~]# hash
hash: hash table empty

[root@c04c ~]# whoami
root

[root@c04c ~]# hash
hits    command
   1    /usr/bin/whoami

[root@c04c ~]# whoami
root

[root@c04c ~]# hash
hits    command
   2    /usr/bin/whoami

Theo các trang man, mục đích của hàm băm là:

Tiện ích / usr / bin / hash ảnh hưởng đến cách môi trường shell hiện tại ghi nhớ các vị trí của các tiện ích được tìm thấy. Tùy thuộc vào các đối số được chỉ định, nó thêm các vị trí tiện ích vào danh sách các vị trí đã nhớ hoặc nó xóa các nội dung của danh sách. Khi không có đối số được chỉ định, nó báo cáo về nội dung của danh sách. Các -rtùy chọn làm cho vỏ quên mọi vị trí nhớ.

Các tiện ích được cung cấp dưới dạng tích hợp vào vỏ không được báo cáo bằng hàm băm.

Ngoài việc xem tôi đã nhập lệnh bao nhiêu lần, tôi không thể thấy tiện ích của hash.

Nó thậm chí còn được giới thiệu trong 15 lệnh hữu ích hàng đầu của thegeek ware.com

Những cách nào là hashhữu ích?

Câu trả lời:


97

hashlà một lệnh bash tích hợp. Bảng băm là một tính năng bash giúp nó không phải tìm kiếm $PATHmỗi khi bạn gõ lệnh bằng cách lưu kết quả vào bộ nhớ. Bảng được xóa trên các sự kiện rõ ràng làm mất hiệu lực kết quả (chẳng hạn như sửa đổi $PATH)

Các hashlệnh chỉ là cách bạn tương tác với hệ thống (đối với bất cứ lý do nào bạn cảm thấy bạn cần).

Một số trường hợp sử dụng:

  • Giống như bạn đã thấy nó in ra bao nhiêu lần bạn nhấn lệnh nào nếu bạn gõ nó không có đối số. Điều này có thể cho bạn biết những lệnh bạn sử dụng thường xuyên nhất.

  • Bạn cũng có thể sử dụng nó để ghi nhớ các tệp thực thi ở các vị trí không chuẩn.

Thí dụ:

[root@policyServer ~]# hash -p /lol-wut/whoami whoami
[root@policyServer ~]# whoami
Not what you're thinking
[root@policyServer ~]# which whoami
/usr/bin/whoami
[root@policyServer ~]# /usr/bin/whoami
root
[root@policyServer ~]#

Điều này có thể hữu ích nếu bạn chỉ có một tệp thực thi trong một thư mục bên ngoài $PATHmà bạn muốn chạy bằng cách chỉ cần nhập tên thay vì bao gồm mọi thứ trong thư mục đó (sẽ có hiệu lực nếu bạn thêm nó vào $PATH).

Một bí danh thường cũng có thể làm điều này, và vì bạn đang sửa đổi hành vi của trình bao hiện tại, nên nó không được ánh xạ trong các chương trình bạn khởi động. Một liên kết tượng trưng đến thực thi đơn độc có lẽ là tùy chọn thích hợp hơn ở đây. hashlà một cách để làm điều đó.

  • Bạn có thể sử dụng nó để bỏ nhớ đường dẫn tập tin. Điều này rất hữu ích nếu một tệp thực thi mới xuất hiện trong một PATHthư mục trước đó hoặc được mvchuyển đến một nơi khác và bạn muốn buộc bash đi ra ngoài và tìm lại nó thay vì nơi cuối cùng nó nhớ tìm thấy nó.

Thí dụ:

[root@policyServer ~]# hash
hits    command
   1    /bin/ls
[root@policyServer ~]# cp /bin/ls /lol-wut
[root@policyServer ~]# hash
hits    command
   1    /bin/cp
   1    /bin/ls
[root@policyServer ~]# hash -d ls
[root@policyServer ~]# ls
default.ldif  newDIT.ldif  notes.txt  users.ldif
[root@policyServer ~]# hash
hits    command
   1    /bin/cp
   1    /lol-wut/ls
[root@policyServer ~]#

Các cplệnh gây ra một phiên bản mới của lsthực thi xuất hiện trước đó trong tôi $PATHnhưng không kích hoạt một cuộc thanh trừng của bảng băm. Tôi đã sử dụng hash -dđể chọn lọc mục nhập lstừ bảng băm. Bash sau đó bị buộc phải xem $PATHlại một lần nữa và khi đó, nó đã tìm thấy nó ở vị trí mới hơn (trước đó là $ PATH so với trước đây).

$PATHMặc dù vậy, bạn có thể gọi một cách có chọn lọc hành vi "tìm vị trí thực thi mới từ " này:

[root@policyServer ~]# hash
hits    command
   1    /bin/ls
[root@policyServer ~]# hash ls
[root@policyServer ~]# hash
hits    command
   0    /lol-wut/ls
[root@policyServer ~]#

Bạn hầu như chỉ muốn làm điều này nếu bạn muốn một cái gì đó từ bảng băm và không 100% rằng bạn có thể đăng xuất và sau đó quay lại thành công hoặc bạn muốn duy trì một số sửa đổi bạn đã thực hiện cho trình bao của mình.

Để loại bỏ ánh xạ cũ, bạn cũng có thể thực hiện hash -r(hoặc export PATH=$PATH) một cách hiệu quả chỉ cần thanh trừng toàn bộ bảng băm của bash.

Có rất nhiều tình huống nhỏ như thế. Tôi không biết nếu tôi gọi nó là một trong những lệnh "hữu ích nhất" nhưng nó có một số trường hợp sử dụng.


Tôi tự hỏi cái tên 'băm' bắt nguồn từ đâu. Thế còn "cache" thì sao? :).
Michael

2
@Michael Bởi vì hashlệnh bên trong sử dụng bảng băm để lưu trữ ánh xạ. vi.wikipedia.org/wiki/Hash_table
jlliagre

10
Cần lưu ý rằng hashkhông bashcụ thể, lệnh bắt nguồn từ shell Bourne trong SVR2 (mặc dù tính năng băm các đường dẫn của các lệnh xuất phát từ cshtrước đó) và được tìm thấy trong tất cả các shell của Bourne và POSIX.
Stéphane Chazelas

1
Thay vì export PATH=$PATHđể xóa bảng, hash -rnên đủ.
ravron

1
Một trường hợp sử dụng khác là khi cài đặt bản sao thứ hai của chương trình vào phần trước của $ PATH của bạn. Bạn cần hash -rhoặc bạn sẽ có được phiên bản cũ vì $ PATH không thay đổi và vì vậy Bash không nhận ra rằng nó có thể tải cùng một chương trình từ thư mục trước đó (ưu tiên cao hơn). Xem conda.pydata.org/docs/ đá để biết chi tiết.
John Zwinck

37

Đây là cách sử dụng cổ điển, được đơn giản hóa:

# My PATH contains /home/rici/bin as well as the Usual Suspects:
# (the real one has lots more)
$ echo $PATH
/home/rici/bin:/usr/local/bin:/usr/bin:/bin

# I've installed a program called hello in /usr/local/bin
$ $ cat /usr/local/bin/hello
#!/bin/bash

echo Hello, world. I live at $0

# The program works.
$ hello
Hello, world. I live at /usr/local/bin/hello

# Now I want to create a better hello, just for me. I put it in
# my own bin directory, and according to my PATH, it should come first.
$ cp /usr/local/bin/hello ~/bin/hello

# So now I will try running it
$ hello
Hello, world. I live at /usr/local/bin/hello

# WTF? Oh, forgot to run hash.
# Tell bash to update where to look for hello
$ hash hello
$ hello
Hello, world. I live at /home/rici/bin/hello

# Ah, all is well.

Như đã lưu ý ở đây, cập nhật có chọn lọc của bảng băm có thể được gọi bằng một lệnh duy nhất hash hello.
Ioannis Filippidis

@johntex: ok, đã thay đổi.
rici

Hãy tưởng tượng tiềm năng cho các lỗi kỳ lạ trước khi biết rằng bảng băm tồn tại! Có một danh sách các trường hợp khi bảng băm tự động được làm mới?
benjimin

@benji: nó không bao giờ được làm mới tự động (nói chung). Nếu bạn chạy bash ở chế độ posix hoặc setopt -s checkhashthực thi băm cho một lệnh không còn tồn tại, mục băm cho lệnh đó sẽ được cập nhật. Nhưng lưu ý rằng mỗi phiên bash đều có bảng băm riêng, do đó, đóng phiên và bắt đầu một phiên mới làm trống hiệu quả băm. ( hash -rLà một cách dễ dàng hơn để làm điều đó.)
rici

Cập nhật $PATHhoặc khởi chạy một thiết bị đầu cuối bash mới, cả hai dường như để dọn sạch bảng.
benjimin

17

Đây là một cách sử dụng hữu ích của hash:

hash php 2> /dev/null || hash -p /usr/local/foobar/php/bin/php php 2> /dev/null

Nó có nghĩa là: nếu php không có trong PATH, thì hãy sử dụng

/usr/local/foobar/php/bin/

8

Có, Hướng dẫn tham khảo Bash nói:

Một tìm kiếm đầy đủ của các thư mục trong $ PATH chỉ được thực hiện nếu không tìm thấy lệnh trong bảng băm.

Nhưng bạn có thể vô hiệu hóa băm với set +h:

-h - Xác định vị trí và ghi nhớ (băm) các lệnh khi chúng được tra cứu để thực thi. Tùy chọn này được kích hoạt theo mặc định.

Thử:

set +h
hash # prints bash: hash: hashing disabled
echo $? # prints 1

Tương tự là cho hash -r, hash NAMEvv

Một "lệnh phát hiện" (như này hay đó ) không hoạt động:

set -h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2 # prints nothing

set +h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2 # prints Please install ls

Bạn có thể viết một cái gì đó như thế này:

old_options="$-"
set -h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
[[ "$old_options" =~ "h" ]] || set +h

hoặc (cảm ơn @mikeerv) mà không phải gán bất kỳ biến mới nào hoặc thực hiện bất kỳ kiểm tra nào:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"

1
Đối với old_optionsđiều của bạn - Tôi thường làm một cái gì đó như thế này: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"vì vậy tất cả sẽ tự động rơi vào vị trí mà không phải gán bất kỳ biến mới hoặc thực hiện bất kỳ kiểm tra hoặc bất cứ điều gì.
mikeerv 29/07/2015

5

Dễ dàng phát hiện xem một lệnh có sẵn hay không:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
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.