Вопрос или проблема
Примечание: {return} в примерах означает: нажмите клавишу возврата
Существует два случая, в которых я не понимаю, почему bash ведет себя так, как он ведет себя в отношении проверки синтаксиса и запроса новой строки.
Случай 1
bash может выполнить строку ( (ls) | cat)
, но в зависимости от того, когда вы нажмете return во время ввода, она может сработать или не сработать.
Это работает:
( (ls ) |{return}
cat
Это не работает:
( (ls) {return}
| cat)
с ошибкой
-bash: ошибка синтаксиса рядом с неожиданным токеном `|’
Есть ли логика, почему второй случай не сработал? Или это просто то, как bash работает внутри?
Случай 2
Еще одна вещь, которую я не понимаю, это то, что, когда вы вводите (ls) "{return}
, bash запрашивает еще одну строку. Есть ли способ завершить эту команду, не получая ошибку синтаксиса? И если нет, то почему bash не выводит сразу сообщение об ошибке синтаксиса?
Вы бы хотели подумать об этом немного в терминах “что программа делает, когда она сканирует то, что я отправил, нажав enter”. В некоторых случаях она начинает новую “вещь” (например, c
в cat
: это явно начало какого-то “слова”, которое оболочка должна понять), некоторые символы просто продолжают существующую “вещь” (например, c
, продолжающий начатую “вещь” с c
), а некоторые вещи завершают ранее начатые “вещи” и помещают их в кучу завершенных “вещей” (например, пробел после cat
, который делает ясно, что “вещь” cat
не может продолжиться).
Эти “вещи” выше будут называться “токены” в науке парсеров/лексеров.
Есть ли логика, почему второй случай не сработал? Или это просто то, как bash работает внутри?
Это так работает синтаксис оболочки, да; наличие открытой, непарной скобки не начинает токен, “поглощающий” новую строку. Однако |
действительно начинает новый токен, который поглощает все пробелы и новые строки после.
Таким образом, “логика” такова: так определено, что должна работать оболочка! Извините.
Еще одна вещь, которую я не понимаю, это то, что, когда вы вводите (ls) “{return} bash запрашивает еще одну строку. Есть ли способ завершить эту команду, не получая ошибку синтаксиса?
ваша "
начинает ввод литеральной строки, так что пока вы не наберете другой "
, все return, которые вы нажимаете, просто становятся переносами строк в этой литеральной строке.
Вот так:
( (ls) |
cat )
# и
( (ls)
| cat )
побочные оболочки всего лишь отвлечение. Рассмотрим более простой случай:
ls |
cat
# и
ls
| cat
Первый – это конвейер с ls
с левой стороны и cat
с правой стороны. Синтаксис оболочки позволяет новую строку после |
. (И это, вероятно, так, так как всегда так было. Кроме того, это ни в коем случае не двусмысленно, все, что может идти после |
, это другая команда.)
Второй – это команда ls
, за которой следует сломанный конвейер | cat
. Я называю это сломанным конвейером, потому что оператор конвейера |
также требует команду с левой стороны. Здесь нет ничего, что можно было бы сделать, чтобы исправить эту ошибку синтаксиса, так что, например, интерактивная оболочка жалуется на это немедленно, не дожидаясь следующей строки (даже если вы обернули это в ( )
).
В
(ls) "
оболочка даже не доходит до стадии фактической проверки синтаксиса команды. С точки зрения синтаксиса, помещенная в кавычки строка является единственным элементом, и она должна дождаться своего окончания, прежде чем она сможет распознать соответствующий токен и проверить синтаксис. Но да, не похоже, что что-либо на следующей строке может сделать команду допустимым синтаксисом оболочки, поскольку вы не можете поставить простое слово после составной команды побочной оболочки.
Все хорошие ответы и комментарии выше, но я удивлен, что никто не упомянул “символ продолжения”.
Я согласен с тем, что ( )
под-оболочки являются отвлечением, вы можете “исправить” свой сломанный код, добавив один дополнительный символ,
ls
| cat
Становится
ls \
| cat
Ваша другая версия
ls |
cat
работает, потому что оболочка рассматривает символ |
как отдельный токен (как объяснялось в ответе MM) и позволяет ему быть последним “словом” в строке, но будет ждать нажатия клавиши Enter, чтобы завершить чтение ввода. (Возможно, существует лучшее техническое объяснение, но общая идея такова).
shellcheck – хороший инструмент для обнаружения и объяснения ошибок синтаксиса. В этом случае он говорит:
paul: ~ $ shellcheck -s bash -
( (ls)
| cat )
В - строке 1:
( (ls)
^-- SC1073 (ошибка): Не удалось разобрать эту явную побочную оболочку. Исправьте, чтобы разрешить больше проверок.
В - строке 2:
| cat )
^-- SC1072 (ошибка): Ожидалась закрывающая ) для побочной оболочки. Исправьте все упомянутые проблемы и попробуйте снова.
^-- SC1133 (ошибка): Неожиданный старт строки. Если вы разбиваете строки, "https://unix.stackexchange.com/"|/&& должно быть в конце предыдущей.
Для получения дополнительной информации:
https://www.shellcheck.net/wiki/SC1133 -- Неожиданный старт строки. Если brea...
https://www.shellcheck.net/wiki/SC1072 -- Ожидалась закрывающая ) для побочной оболочки. ...
https://www.shellcheck.net/wiki/SC1073 -- Не удалось разобрать эту явную побочную оболочку...
paul: ~ $
Ответ или решение
Понимание Синтаксиса Bash: Разбираем Ошибки и Поведение
При работе с Bash возникают ситуации, когда поведение интерпретатора может сбивать с толку, особенно когда дело доходит до проверки синтаксиса и разбиения строк. Давайте детально рассмотрим две часто возникающие проблемы, связанные с синтаксической проверкой и некорректным вводом.
Случай 1: Ошибка синтаксиса при использовании пайпов
Когда мы вводим команду в Bash, поведение интерпретатора во многом зависит от расположения символов и пробелов. Рассмотрим команды, обсуждаемые в первом случае:
-
Работающая команда:
( (ls) | cat )
-
Несрабатывающая команда:
( (ls) | cat)
В этом случае мы получаем ошибку:
-bash: syntax error near unexpected token `|'
Анализ: В первом примере открывающая скобка и команда ls
образуют подшелл, который корректно завершается, когда Bash видит | cat
. Это позволяет интерпретатору распознавать полное выражение. Во втором случае, разрыв перед пайпом |
приводит к тому, что Bash не может интерпретировать конструкцию как завершённую, поскольку перед пайпом отсутствует команда. В то время как |
сам по себе воспринимается как новый токен, который "ждет" следующую команду, и, следовательно, ожидает ввода новой строки.
Случай 2: Строковые литералы и ожидание новой строки
Во втором случае, когда мы вводим:
(ls) "
Bash ожидает завершения строкового литерала. Несмотря на то что казалось бы недочет, это становится ясным, когда мы понимаем, что символ "
сигнализирует началу строкового выражения. Bash будет ждать закрывающей кавычки, чтобы завершить ввод:
Объяснение:
- Открывающая кавычка инициирует ввод строкового литерала, и Bash ожидает, что вы введете соответствующую закрывающую кавычку, прежде чем сможет проверить синтаксис.
- Если никакой логической конструкции не следует после открывающей скобки, Bash не может определить, как завершить команду, и, соответственно, не выдает ошибку до тех пор, пока не будет введена закрывающая кавычка.
Выводы: Как избежать ошибок
Чтобы избежать подобных ошибок, следует учитывать:
- Общее правило: Каждая конструкция (например, подшелл или пайп) должна быть завершена до ввода новой строки.
- Имейте в виду открывающие скобки и кавычки: Эти элементы ожидают завершения. Обе конструкции имеют разное поведение, и одна не может служить заменой для другой.
- Используйте символ продолжения: Вы можете явно указать, что команда продолжается, используя символ обратного слэша
\
. Например:ls \ | cat
Это даст понять интерпретатору, что ввод команды не завершен.
Инструменты для проверки синтаксиса
Для более глубокого анализа и выявления синтаксических ошибок в скриптах стоит воспользоваться инструментами, такими как ShellCheck. Этот инструмент поможет диагностировать не только стандартные ошибки, но и предложит информативные советы для улучшения и оптимизации кода.
Заключение
Понимание работы синтаксиса в Bash — задача, требующая практики и внимательного отношения к деталям. Изучение поведения интерпретатора поможет избежать распространенных ошибок и повысить уверенность в написании сценариев. Если у вас остаются вопросы, рекомендуется обратиться к документации или сообществу пользователей Bash для получения дополнительной помощи.