Как find заменяет {}, если он содержит специальные символы, такие как "?

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

Недавно, когда я читал один вопрос-ответ, я заметил некоторые неожиданные для меня поведения:

~/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.

Оцените материал
?

">
Добавить комментарий

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