Apa tujuan dari perintah hash?

118

Jika Anda menjalankannya hashmenunjukkan path dari semua perintah yang dijalankan sejak hash adalah reset terakhir ( 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

Menurut halaman manual, tujuan hash adalah:

Utilitas / usr / bin / hash mempengaruhi cara lingkungan shell saat ini mengingat lokasi utilitas yang ditemukan. Bergantung pada argumen yang ditentukan, ia menambah lokasi utilitas ke daftar lokasi yang diingatnya atau membersihkan isi daftar. Ketika tidak ada argumen yang ditentukan, itu melaporkan isi daftar. The -rmenyebabkan shell untuk melupakan semua lokasi diingat.

Utilitas yang disediakan sebagai built-in ke shell tidak dilaporkan oleh hash.

Selain melihat berapa kali saya memasukkan perintah, saya tidak bisa melihat utilitas hash.

Itu bahkan ditampilkan dalam 15 perintah berguna thegeekstuff.com

Dalam hal apa hashbermanfaat?

spuder
sumber

Jawaban:

97

hashadalah perintah built-in bash. Tabel hash adalah fitur bash yang mencegahnya dari harus mencari $PATHsetiap kali Anda mengetik perintah dengan menyimpan hasilnya di memori. Tabel akan dihapus pada acara yang jelas-jelas membatalkan hasil (seperti memodifikasi $PATH)

The hashperintah hanya bagaimana Anda berinteraksi dengan sistem yang (untuk mana alasan Anda merasa Anda perlu).

Beberapa kasus penggunaan:

  • Seperti Anda melihatnya mencetak berapa kali Anda menekan perintah mana jika Anda mengetiknya tanpa argumen. Ini mungkin memberi tahu Anda perintah mana yang paling sering Anda gunakan.

  • Anda juga dapat menggunakannya untuk mengingat file yang dapat dieksekusi di lokasi yang tidak standar.

Contoh:

[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 ~]#

Yang mungkin berguna jika Anda hanya memiliki satu executable di direktori di luar $PATHyang ingin Anda jalankan dengan cukup ketik nama daripada memasukkan semua yang ada di direktori itu (yang akan menjadi efek jika Anda menambahkannya ke $PATH).

Alias ​​biasanya dapat melakukan ini juga, meskipun dan karena Anda memodifikasi perilaku shell saat ini, itu tidak dipetakan dalam program yang Anda mulai. Symlink ke executable tunggal mungkin merupakan opsi yang lebih disukai di sini. hashadalah salah satu cara untuk melakukannya.

  • Anda dapat menggunakannya untuk menghapus lintasan file. Ini berguna jika sebuah executable baru muncul di PATHdirektori sebelumnya atau pergi mvke tempat lain dan Anda ingin memaksa bash untuk keluar dan menemukannya lagi, bukan tempat terakhir yang ia ingat menemukannya.

Contoh:

[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 ~]#

The cpperintah menyebabkan versi baru dari lseksekusi muncul di awal saya $PATHtetapi tidak memicu pembersihan dari tabel hash. Saya digunakan hash -duntuk membersihkan entri secara selektif lsdari tabel hash. Bash kemudian dipaksa untuk melihat $PATHlagi dan ketika itu terjadi, ia menemukannya di lokasi yang lebih baru (lebih awal dalam $ PATH daripada yang berjalan sebelumnya).

Anda dapat secara selektif menjalankan $PATHperilaku "cari lokasi yang dapat dieksekusi dari " ini, meskipun:

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

Anda sebagian besar hanya ingin melakukan ini jika Anda menginginkan sesuatu dari tabel hash dan tidak 100% Anda bisa logout dan kemudian berhasil kembali, atau Anda ingin mempertahankan beberapa modifikasi yang telah Anda buat pada shell Anda.

Untuk menghilangkan pemetaan basi, Anda juga dapat melakukan hash -r(atau export PATH=$PATH) yang secara efektif hanya membersihkan seluruh tabel hash bash.

Ada banyak situasi kecil seperti itu. Saya tidak tahu apakah saya akan menyebutnya salah satu dari perintah "paling berguna" tetapi memiliki beberapa kasus penggunaan.

Bratchley
sumber
Saya bertanya-tanya dari mana nama 'hash' berasal. Bagaimana dengan misalnya "cache", lalu? :)
Michael
2
@Michael Karena hashperintah secara internal menggunakan tabel hash untuk menyimpan pemetaan. en.wikipedia.org/wiki/Hash_table
jlliagre
10
Perlu dicatat bahwa hashtidak bashspesifik, perintah berasal dari shell Bourne di SVR2 (meskipun fitur hashing paths dari perintah berasal dari cshsebelumnya) dan ditemukan di semua shell Bourne-like dan POSIX.
Stéphane Chazelas
1
Alih-alih export PATH=$PATHmembersihkan meja, hash -rharus cukup.
ravron
1
Kasus penggunaan lain adalah ketika menginstal salinan kedua program ke bagian sebelumnya dari $ PATH Anda. Anda perlu hash -ratau Anda akan mendapatkan versi yang lama karena $ PATH tidak berubah sehingga Bash tidak menyadari bahwa ia dapat memuat program yang sama dari direktori sebelumnya (prioritas yang lebih tinggi). Lihat conda.pydata.org/docs/… untuk detailnya.
John Zwinck
37

Inilah penggunaan klasik, disederhanakan:

# 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.
rici
sumber
Seperti disebutkan di sini, pembaruan selektif tabel hash dapat dipanggil dengan satu perintah hash hello.
Ioannis Filippidis
@johntex: ok, berubah.
rici
Bayangkan potensi bug aneh sebelum mengetahui bahwa tabel hash ada! Apakah ada daftar keadaan ketika tabel hash secara otomatis disegarkan?
benjimin
@benji: itu tidak pernah disegarkan secara otomatis (secara keseluruhan). Jika Anda menjalankan bash dalam mode posix atau setopt -s checkhashdan hash yang dapat dieksekusi untuk perintah sudah tidak ada, entri hash untuk perintah itu akan diperbarui. Tetapi perhatikan bahwa setiap sesi bash memiliki tabel hash sendiri, jadi menutup sesi dan memulai yang baru secara efektif mengosongkan hash. ( hash -radalah cara yang lebih mudah untuk melakukan itu.)
rici
Memperbarui $PATH, atau meluncurkan terminal bash baru, keduanya tampak membersihkan meja.
benjimin
17

Inilah penggunaan yang bermanfaat hash:

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

Artinya: jika php tidak ada dalam PATH, maka gunakan

/usr/local/foobar/php/bin/
Gilles Quenot
sumber
8

Ya, Manual Referensi Bash mengatakan:

Pencarian penuh direktori di $ PATH dilakukan hanya jika perintah tidak ditemukan di tabel hash.

Tetapi Anda dapat menonaktifkan hashing dengan set +h:

-h - Temukan dan ingat perintah (hash) saat mereka dicari untuk dieksekusi. Opsi ini diaktifkan secara default.

Mencoba:

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

Hal yang sama juga berlaku untuk hash -r, hash NAMEdll

"Deteksi perintah" (seperti ini atau itu ) tidak berfungsi:

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

Anda dapat menulis sesuatu seperti ini:

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

atau (terima kasih kepada @mikeserv) tanpa harus menetapkan variabel baru atau melakukan tes apa pun:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
Evgeny Vereshchagin
sumber
1
Untuk old_optionshal Anda - saya biasanya melakukan sesuatu seperti ini: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"jadi semuanya jatuh ke tempatnya secara otomatis tanpa harus menetapkan variabel baru atau melakukan tes atau apa pun.
mikeserv
5

Deteksi mudah apakah perintah tersedia:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
brablc
sumber