grep возвращает “Нет такого файла или директории” после подстановки команд

Вопрос или проблема

Я пытаюсь использовать 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. В результате вы не видите ожидаемого вывода, и на экране не отображается результат команды.

Как решить проблемы

  1. Используйте правильный формат переменных:
    Убедитесь, что выводите именно ту переменную, в которую записали результат:

    echo "${RESUlT}"  # Исправлено на правильное имя переменной
  2. Избегайте использования символов, вызывающих проблемы:
    Если вы хотите сохранить вывод grep, и убедиться, что он не будет трактоваться как команда для выполнения, добавьте к полученному результату | cat:

    RESUlT=$(grep -R -I --exclude-dir=addons "^[^#]*print(" | cat)

Это просто обозначает, что вы хотите вывести результаты в cat, которая, в свою очередь, не изменит данные, и более безопасно для обработки, так как предотвращает возможные проблемы с интерпретацией выходных данных.

Заключение

Ошибка "Нет такого файла или каталога" возникает из-за неправильной интерпретации завершённого вывода команды grep как команды для выполнения. Убедившись, что все переменные правильно названы и избегая использования символов, вызывающих путаницу, можно устранить эту проблему. Таким образом, ваш скрипт будет работать корректно и операции над переменными будут выполняться без ошибок.

Оцените материал
Добавить комментарий

Капча загружается...