Вопрос или проблема
Недавно, когда я читал один вопрос-ответ, я заметил некоторые неожиданные для меня поведения:
~/findtest % echo three > file\ with\ \"double\ quotes\"
~ % find findtest -type f -exec sh -c 'set -x;cat "{}"' \;
+ cat 'findtest/file with double' quotes
cat: findtest/file with double: Нет такого файла или каталога
cat: quotes: Нет такого файла или каталога
На мой взгляд, при замене, это сделает 'cat findtest/file\ with\ \"double\ quotes\"'
, если просто заменить вышеупомянутое имя файла file\ with\ \"double\ quotes\"
, что сработает. Если сохранить обрамление кавычками, это покажет:
$ cat "findtest/file\ with\ \"double\ quotes\""
cat: 'findtest/file\ with\ "double\ quotes"': Нет такого файла или каталога
Вопрос:
Что именно делает эта замена, вызывая вышеуказанное немного странное поведение с двумя подстроками ‘findtest/file with double’ и ‘quotes’, но не ‘findtest/file with’ и ‘double quotes’, с "
в качестве разделителя?
Поведение, которое вы наблюдаете, не является результатом обработки “специальных символов” (кавычки не являются специальными для find
), это результат обработки sh
. Имя файла file with "double quotes"
; обратные косые черты — это экранирование, предназначенное для оболочки, они не появляются в файловой системе, и find
о них не знает.
Таким образом
$ find findtest -type f -exec cat {} \;
работает как ожидается: find
находит findtest/file with "double quotes"
и формирует вызов cat
с этим аргументом; cat
открывает его, и все работает.
Теперь, когда вы добавляете sh
в смесь, find
делает то же самое: с
find findtest -type f -exec sh -c 'set -x;cat "{}"' \;
find
создает вызов sh
с аргументами -c
, за которыми следует set -x; cat "findtest/file with "double quotes""
. sh
обрабатывает кавычки, в результате чего вызов cat
производится с аргументами findtest/file with double
и quotes
:
cat "findtest/file with "double quotes""
^ ^ ^^
\___________________/ \/
Пары кавычек ограничивают findtest/file with
(с пробелом в конце) и пустую строку. Поскольку между второй двойной кавычкой и double
нет пробела, это конкатенируется в том же аргументе; пробел между double
и quotes
не встречается внутри пары кавычек, поэтому он разделяет слова. Таким образом, после удаления кавычек и разделения по словам, компоненты командной строки являются
cat
findtest/file with double
quotes
Ответ или решение
Вопрос, рассматриваемый в приведенном вами примере, вызывает интересные особенности о том, как команда find
взаимодействует с оболочкой (в данном случае, sh
) при обработке специальных символов, включая кавычки. Давайте глубже погрузимся в этот процесс с точки зрения технической теории, практического примера и применения.
Теория
Команда find
в UNIX-подобных системах используется для поиска файлов и каталогов, удовлетворяющих заданным критериям. Однако, когда в командную строку включается -exec
, это добавляет уровень сложности, особенно при наличии символов, которые являются "специальными" для оболочки, таких как кавычки или обратные слэши.
Важно понимать, что символы, используемые для экранирования в оболочке (например, обратный слэш \ перед пробелами или кавычками), не имеют значения для самой файловой системы. Это просто способ для оболочки интерпретировать текст в командной строке правильно.
Для команды find
, {}
является простым маркером, который указывает, где вставить имя найденного файла в команду. Однако, когда {}
передается как часть строки в exec
, оно передается оболочке как часть команды. Оболочка должна затем интерпретировать эту строку, и здесь вступает в игру синтаксис оболочки.
Пример
Рассмотрим пример:
find findtest -type f -exec sh -c 'set -x; cat "{}"' \;
Итак, что происходит за кулисами? Когда find
находит файл с именем file with "double quotes"
, она подставляет его в место {}
таким образом, что командная строка становится:
sh -c 'set -x; cat "findtest/file with "double quotes""'
Здесь начинается основная трудность. sh
обрабатывает строки и обнаруживает кавычки. Оно интерпретирует первый набор двойных кавычек как начало и конец первой "строки" (которая фактически содержит пробел), а затем разбивает строку, когда встречает следующее слово после кавычек, то есть, "double
, что приводит к следующему разделению:
cat
findtest/file with double
quotes
Применение
Ошибка в данном контексте заключается в неверной интерпретации того, как оболочка обрабатывает кавычки при разборе строки. Оболочка sh
удаляет кавычки и разделяет строку на части на основе пробелов, которые не находятся в кавычках, что приводит к некорректному разбиению аргументов для команды cat
.
Как можно исправить ситуацию? Для корректной работы скрипта необходимо внимательно учитывать синтаксис оболочки. Одним из решений может быть использование механизма передачи параметров через массив аргументов, чтобы избежать подобных проблем с интерпретацией. Например:
find findtest -type f -exec sh -c 'for file; do cat "$file"; done' _ {} \;
Здесь промежуточный символ _
используется для заполнения позиции $0
, а аргументы передаются как отдельные слова. Использование конструкции for file; do ... done
, позволяет безошибочно обрабатывать файл именами, содержащими пробелы и специальные символы, за счет использования кавычек вокруг $file
.
Для ИТ-специалистов понимание взаимодействия между оболочкой и инструментами, такими как find
, является крайне важным навыком. Вы должны учитывать, как различные уровни оболочки обрабатывают специальные символы и как можно манипулировать ими, чтобы автоматизировать задачи с минимальным количеством ошибок.
Таким образом, каждый раз, когда вы работаете с инструментами, взаимодействующими с оболочками и файловой системой, продуманная обработка строк и внимательное отношение к синтаксису поможет избежать неприятных сюрпризов. Надеюсь, это подробное объяснение окажется полезным для более глубокого понимания работы оболочки и команд UNIX.