Заметка дня #11 Изредка, пробуя установить разные версии одной и той… — Остальные 90% — TG.ME

Заметка дня #11

Изредка, пробуя установить разные версии одной и той же утилиты, можно наткнуться на неожиданное поведение: несмотря на то, что мы только что установили вторую версию приложения, 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
👍18❤9🔥2
December 29, 2025 814 4 16