Какова цель хэш-команды?

118

При запуске hashон показывает путь всех команд, запущенных с момента последнего сброса хеша ( 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

Согласно страницам руководства, целью хэша является:

Утилита / usr / bin / hash влияет на то, как текущая среда оболочки запоминает расположение найденных утилит. В зависимости от указанных аргументов он добавляет служебные местоположения в свой список запоминаемых расположений или удаляет содержимое списка. Если аргументы не указаны, он сообщает о содержимом списка. -rОпция заставляет оболочку забыть все сохраненные места.

Утилиты, предоставляемые как встроенные в оболочку, не передаются хешем.

За исключением того, сколько раз я ввел команду, я не вижу полезности hash.

Он даже был включен в топ-15 полезных команд thegeekstuff.com

Чем это hashполезно?

spuder
источник

Ответы:

97

hashэто встроенная команда bash Хэш-таблица - это функция, bash которая предотвращает необходимость поиска при $PATHкаждом вводе команды путем кэширования результатов в памяти. Таблица очищается от событий, которые явно аннулируют результаты (например, изменение $PATH)

Команда hash- это то, как вы взаимодействуете с этой системой (по той причине, по которой вы чувствуете, что вам нужно).

Некоторые варианты использования:

  • Как вы видели, он печатает, сколько раз вы нажимаете какие команды, если вы вводите его без аргументов. Это может сказать вам, какие команды вы используете чаще всего.

  • Вы также можете использовать его для запоминания исполняемых файлов в нестандартных местах.

Пример:

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

Это может быть полезно, если у вас есть только один исполняемый файл в каталоге, $PATHкоторый вы хотите запустить, просто введите имя, а не включайте все в этот каталог (что будет иметь эффект, если вы добавите его в $PATH).

Псевдоним, как правило, тоже может это делать, хотя, поскольку вы модифицируете поведение текущей оболочки, он не отображается в программах, которые вы запускаете. Символьная ссылка на одиночный исполняемый файл, вероятно, является предпочтительным вариантом здесь. hashэто один из способов сделать это.

  • Вы можете использовать его для запоминания путей к файлам. Это полезно, если новый исполняемый файл появляется в более раннем PATHкаталоге или получает mvкуда-то еще, и вы хотите заставить bash выйти и найти его снова, а не в последнем месте, которое он запомнил, найдя его.

Пример:

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

Команда cpвызвала появление новой версии lsисполняемого файла ранее в моем, $PATHно не запустила очистку хеш-таблицы. Раньше я hash -dвыборочно удалял запись lsиз хеш-таблицы. Затем Bash был вынужден просмотреть $PATHснова, и когда он это сделал, он нашел его в более новом месте (ранее в $ PATH, чем раньше).

Вы можете выборочно вызвать это $PATHповедение «найти новое расположение исполняемого файла из », хотя:

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

В большинстве случаев вы просто захотите сделать это, если вы хотите что-то из хеш-таблицы и не на 100% можете выйти из системы, а затем вернуться обратно или хотите сохранить некоторые изменения, внесенные в вашу оболочку.

Чтобы избавиться от устаревших отображений, вы также можете сделать hash -r(или export PATH=$PATH), который эффективно просто удаляет всю хэш-таблицу bash.

Есть много таких маленьких ситуаций. Я не знаю, назову ли я это одной из «самых полезных» команд, но у нее есть некоторые варианты использования.

Bratchley
источник
Интересно, откуда появилось название «хэш». Как насчет, например, «кеша»? :).
Майкл
2
@Michael Поскольку hashкоманда внутренне использует хеш-таблицу для хранения отображений. en.wikipedia.org/wiki/Hash_table
jlliagre
10
Следует отметить, что hashэто не является bashспецифическим, команда, созданная в оболочке Bourne в SVR2 (хотя функция хеширования путей команд появилась cshранее), встречается во всех оболочках Bourne-like и POSIX.
Стефан Шазелас
1
Вместо того, export PATH=$PATHчтобы очистить таблицу, hash -rдолжно хватить.
Раврон
1
Другой вариант использования - при установке второй копии программы в более раннюю часть вашего $ PATH. Вам нужно hash -rили вы получите старую версию, потому что $ PATH не изменился, и поэтому Bash не осознает, что может загрузить ту же программу из более раннего каталога (с более высоким приоритетом). Смотрите conda.pydata.org/docs/… для подробностей.
Джон Цвинк
37

Вот классическое упрощенное использование:

# 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
источник
Как отмечалось здесь, выборочное обновление хеш-таблицы может быть вызвано одной командой hash hello.
Иоаннис Филиппидис
@johntex: хорошо, изменилось.
Ричи
Вообразите потенциал для странных ошибок, прежде чем знать, что хеш-таблица существует! Есть ли список обстоятельств, когда хеш-таблица автоматически обновляется?
Бенджимин
@benji: он никогда не обновляется автоматически (в целом). Если вы запустите bash в режиме posix или setopt -s checkhashесли хешированный исполняемый файл для команды больше не существует, хеш-запись для этой команды будет обновлена. Но обратите внимание, что у каждого сеанса bash была своя собственная хеш-таблица, поэтому закрытие сеанса и запуск нового эффективно очищает хеш. ( hash -rэто более простой способ сделать это.)
Ричи
Обновление $PATHили запуск нового терминала bash, оба, кажется, очищают таблицу.
Бенджимин
17

Вот полезное использование hash:

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

Это означает: если php отсутствует в PATH, используйте

/usr/local/foobar/php/bin/
Жиль Квено
источник
8

Да, Справочное руководство Bash гласит:

Полный поиск по каталогам в $ PATH выполняется только в том случае, если команда не найдена в хеш-таблице.

Но вы можете отключить хеширование с помощью set +h:

-h - Найдите и запомните (хеш) команды, когда они ищутся для выполнения. Эта опция включена по умолчанию.

Пытаться:

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

То же самое для hash -rи hash NAMEт. Д.

«Обнаружение команды» (вроде того или другого ) не работает:

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

Вы можете написать что-то вроде этого:

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

или (спасибо @mikeserv) без необходимости присваивать новые переменные или выполнять какие-либо тесты:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
Евгений Верещагин
источник
1
Для твоего old_optionsдела - я обычно делаю что-то вроде этого: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"так что все просто становится на свои места автоматически, без необходимости присваивать какие-либо новые переменные или делать какие-либо тесты или что-то еще.
mikeserv
5

Легко определить, доступна ли команда:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
brablc
источник