Вопрос или проблема
Я пытаюсь использовать grep в bash-скрипте и присваивать результат переменной:
RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
Но он возвращает
bash: Map/Places/place.gd:: Нет такого файла или каталога
Я протестировал это в своем терминале и
grep -R -I --exclude-dir=addons "^[^#]*print("
возвращает то, что я ожидаю, но
$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
возвращает ошибку.
У меня нет понятия, почему это происходит.
Мой полный скрипт:
#!/bin/bash
CYAN='\033[0;36m'
RED='\033[0;31m'
NC='\033[0m' # Без цвета
RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
echo "${RESULT}"
echo -e "${CYAN}Я ${RED}люблю${NC} Stack Overflow"
Что делает bash:
**bash -o xtrace .check_code.sh**
+ CYAN='\033[0;36m'
+ RED='\033[0;31m'
+ NC='\033[0m'
++ grep -R -I --exclude-dir=addons '^[^#]*print('
+ RESUlT='Map/Places/place.gd: print("enter")'
+ echo ''
+ echo -e '\033[0;36mЯ \033[0;31mlюблю\033[0m Stack Overflow'
Я люблю Stack Overflow
Одна странная вещь в терминале:
whereis grep
возвращает
grep: /usr/bin/grep /usr/share/man/man1/grep.1.gz /usr/share/info/grep.info.gz
Но:
$(whereis /usr/bin/grep)
возвращает
Команда 'grep:' не найдена, вы имели в виду:
команда 'grep' из deb grep (3.11-2)
Попробуйте: sudo apt install <имя пакета>
Ничего странного, просто работа подстановки команд.
Когда вы протестировали команду
$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
шелл сначала выполнил подстановку команд в строке команд перед тем, как фактически попытаться выполнить строку команд после преобразования её с помощью подстановки команд.
Из вашего bash -o xtrace ...
вывода, я вижу, что выполнение команды в скобках дает выход:
Map/Places/place.gd: print("enter")
Другими словами, grep
нашел не закомментированную строку print(
в файле с именем Map/Places/place.gd
, как часть строки, содержащей print("enter")
.
Затем вывод grep
вставляется на место выражения подстановки команд $(
… )
в исходной строке команд. Поскольку вся строка команд заключена в $(
… )
, вся строка команд заменяется на указанный вывод.
Затем шелл пытается выполнить преобразованную строку команд. Поскольку в исходной строке команд не было ничего, кроме выражения подстановки команд, шелл в конечном итоге пытается выполнить вывод команды, который был в выражении подстановки команд как фактическую команду.
Другими словами, он попытается найти исполняемый файл с относительным путем Map/Places/place.gd:
и передаст ему print("enter")
в качестве первого аргумента.
Вероятно, у вас нет файла с именем place.gd:
(с двоеточием в имени файла). Поскольку такого файла не существует, шелл отображает сообщение об ошибке. На самом деле это говорит:
“Я bash
, и у меня проблема с Map/Places/Place.gd:
, которую вы только что сказали мне выполнить: Нет такого файла или каталога
.”
Он просто отображает это в несколько более сжатой форме:
bash: Map/Places/place.gd:: Нет такого файла или каталога
В вашем скрипте обратите внимание на эти две строки:
RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
echo "${RESULT}"
Вывод команды в выражении подстановки команд $(
… )
сохраняется в переменной оболочки RESUlT
. Затем, в следующей строке, вы говорите шеллу вывести содержимое другой переменной RESULT
. Да, они разные: обратите внимание на строчную l
в имени первой переменной.
“Одна странная вещь в терминале” такая же, как ошибка в вашем первом тесте терминала.
Первая строка команд
whereis grep
возвращает вывод:
grep: /usr/bin/grep /usr/share/man/man1/grep.1.gz /usr/share/info/grep.info.gz
Вторая строка команд
$(whereis /usr/bin/grep)
сначала выполнит ту же команду, что и первая строка команд, захватит ее вывод, заменит выражение подстановки команд выводом, а затем попытается выполнить результирующую строку команд.
Поскольку строка команд теперь была преобразована в
grep: /usr/bin/grep [...и т.д...]
шелл теперь пытается найти команду grep:
(с двоеточием). Поскольку в “команде” нет символов “/”, ищутся каталоги, указанные в $PATH
, а затем, по-видимому, срабатывает вспомогательная функция из необязательного пакета command-not-found
.
Он выдает более подробное сообщение об ошибке с некоторыми предложениями, определяя, что grep:
может быть grep
с неправильно набранным двоеточием, и если это не та команда, которую вы ищете, вам может потребоваться установить пакет, содержащий команду, которую вы хотите. Но в данном случае эти предложения не применимы, потому что вы фактически попытались вслепую выполнить вывод другой команды.
Ответ или решение
Вопрос, с которым вы столкнулись, связан с особенностями командной подстановки в баш-скриптах. Давайте внимательно рассмотрим, что происходит в вашем скрипте и почему возникает ошибка "Нет такого файла или каталога".
Контекст проблемы
Вы пытаетесь использовать команду grep
для поиска строк, содержащих print(
, и сохраняете результат в переменной RESUlT
:
RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
Однако, когда вы запускаете эту строку, возникает ошибка:
bash: Map/Places/place.gd:: No such file or directory
В то время как если вы просто выполните команду grep
в терминале, ожидаемый результат будет выведен корректно. Давайте разберёмся, почему так происходит.
Объяснение поведения Bash
1. Что делает командная подстановка
Командная подстановка (интерпретируемая как $(команда)
) сначала выполняет указанную команду и затем заменяет её вывод в исходной строке. Таким образом ваш код RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" )
интерпретируется как:
- Выполнение
grep -R -I --exclude-dir=addons "^[^#]*print("
. - Полученный вывод, например,
Map/Places/place.gd: print("enter")
, заменяет команду, в которой он находился.
Из-за того, что grep
возвращает строки, содержащие :
(двоеточие) в результате, Bash трактует это как команду для выполнения. Ваша строка теперь выглядит, как если бы вы попытались выполнить файл с именем Map/Places/place.gd:
с аргументом print("enter")
. Поскольку такого файла не существует (особенно с двоеточием в имени), возникает ошибка.
2. Несоответствие переменных
Также стоит отметить, что в строке echo "${RESULT}"
, вы обращаетесь к переменной с другим именем RESULT
(все буквы в нижнем регистре), тогда как сохранённый результат находится в переменной RESUlT
. В результате вы не видите ожидаемого вывода, и на экране не отображается результат команды.
Как решить проблемы
-
Используйте правильный формат переменных:
Убедитесь, что выводите именно ту переменную, в которую записали результат:echo "${RESUlT}" # Исправлено на правильное имя переменной
-
Избегайте использования символов, вызывающих проблемы:
Если вы хотите сохранить выводgrep
, и убедиться, что он не будет трактоваться как команда для выполнения, добавьте к полученному результату| cat
:RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" | cat)
Это просто обозначает, что вы хотите вывести результаты в cat
, которая, в свою очередь, не изменит данные, и более безопасно для обработки, так как предотвращает возможные проблемы с интерпретацией выходных данных.
Заключение
Ошибка "Нет такого файла или каталога" возникает из-за неправильной интерпретации завершённого вывода команды grep
как команды для выполнения. Убедившись, что все переменные правильно названы и избегая использования символов, вызывающих путаницу, можно устранить эту проблему. Таким образом, ваш скрипт будет работать корректно и операции над переменными будут выполняться без ошибок.