Изредка, пробуя установить разные версии одной и той же утилиты, можно наткнуться на неожиданное поведение: несмотря на то, что мы только что установили вторую версию приложения, Bash упорно отказывается её видеть. В новом же терминале, однако, всё работает корректно, и это может выглядеть подозрительно.
Оказывается, Bash ищет утилиту лишь раз, при первом её вызове - после этого он хранит путь к найденному исполняемому файлу во внутреннем ассоциативном массиве, и все последующие вызовы команды сводятся к вызову файла, расположенному по сохранённому ранее пути. Именно поэтому, если мы вызвали версию утилиты, а затем удалили её, то без явных подсказок Bash новую версию искать не станет.
Посмотрим на поведение Bash на примере: создадим файл
do-not-delete-me.sh по пути /usr/local/bin и убедимся, что всё работает:# Create a file:
$ cat do-not-delete-me.sh
#!/bin/bash
echo 'Hello, Elliot'
# Make it runnable and move to /usr/local/bin:
$ chmod +x do-not-delete-me.sh
$ sudo mv do-not-delete-me.sh /usr/local/bin
# Call the binary!
$ do-not-delete-me.sh
Hello, Elliot
Теперь переместим скрипт в
~/.local/bin, тоже содержащийся в переменной $PATH - технически, Bash по-прежнему должен найти команду, однако этого не происходит:$ sudo mv /usr/local/bin/do-not-delete-me.sh ~/.local/bin
$ do-not-delete-me.sh
bash: /usr/local/bin/do-not-delete-me.sh: No such file or directory
Несложно дополнительно убедиться, что открытый в отдельном окне терминал вызвать команду всё-таки может.
Исправить ситуацию можно, используя встроенную команду Bash
hash, отвечающую за построение ассоциативного массива, речь о котором шла выше. Вызванная без аргументов, она покажет, какие команды были запомнены:$ hash
hits command
4 /usr/local/bin/do-not-delete-me.sh
1 /usr/bin/chmod
3 /usr/bin/ls
...
Для удаления конкретной записи можно использовать ключ
-d, однако самым коротким путём будет очистить всю таблицу одним махом:$ hash -r
Теперь путь до скрипта найдётся корректный, и запуск команды завершится успехом:
$ do-not-delete-me.sh
Hello, Elliot
$ hash
hits command
1 /home/mint/.local/bin/do-not-delete-me.sh
Наткнуться на эту проблему можно, перемежая установку сборок утилиты из репозиториев дистрибутива и GitHub - тот же сканер Grype служит наглядным примером. На момент написания заметки Ubuntu 22 позволяет установить версию 0.99.1, в то время как самый свежий релиз - уже 0.104.3. Мы могли установить сканер из репозиториев, обнаружить старую версию, удалить её и установить новую в другое место - и получить именно эту ошибку.
Проблема стреляет крайне редко, и обычно не причиняет существенных неприятностей, однако помнить о её истоках полезно. Если мы работали удалённо по SSH, и подключение требует ввода пароля, то открывать новое соединение лишний раз обычно не хочется.
#tip



