Проблемы при использовании find
и xargs
Когда вы работаете с утилитами командной строки в Linux, такими как find
и xargs
, вы можете столкнуться с проблемами, связанными с "странными" именами файлов. Одной из распространенных ситуаций является передача имен файлов, содержащих символы, которые чувствительны к оболочке. Например, символы вроде '
, "
, `
, и (
могут вызывать ошибки.
Основные решения включают использование специального разделителя (например, \0
) и цитирование строк, чтобы избежать проблем с интерпретацией символов. Это особенно актуально, когда вы хотите использовать найденные имена файлов в вложенных командах.
Пример проблемы
Рассмотрим следующий сценарий:
mkdir remove_afterwards
touch remove_afterwards/"some [\"strange\"] ('file')"
find remove_afterwards/ -type f -print0 | xargs -0 -I{} sh -c 'echo "{}" $(stat -c "%s" "{}")'
В этом случае, попытка вывести как имя файла, так и его размер с помощью stat
вызывает ошибку:
stat: cannot statx 'remove_afterwards/some [strange] ('\''file'\'')': No such file or directory
При этом, если вы используете команду без вложенного вызова, она может работать:
find remove_afterwards/ -type f -print0 | xargs -0 -I{} sh -c 'echo "{}"'
Анализ проблемы
Эта проблема возникает из-за того, что sh -c
не может правильно интерпретировать аргумент, когда он расширяется с использованием {}
внутри кода оболочки. sh
получает расширенный аргумент и интерпретирует его как код оболочки, что приводит к нестабильному поведению.
Решение проблемы
Безопасный способ решения этой проблемы — передать расширенный {}
как отдельный аргумент, который будет использоваться в качестве позиционного параметра в sh -c
. Пример корректного синтаксиса:
find remove_afterwards/ -type f -print0 | xargs -0 -I{} sh -c 'echo "$1" "$(stat -c "%s" "$1")"' find-sh {}
В этом примере find-sh
— это просто идентификатор, который позволяет sh -c
корректно обрабатывать параметры.
Объяснение решения
Преимущество такого подхода в том, что код в sh -c
остается статическим, и расширяемый {}
передается как позиционный параметр $1
. Это значит, что интерпретатор знает, что это не код, а просто значение. Важно правильно заключать $1
в кавычки, чтобы избежать проблем с пробелами и специальными символами.
Углубление в тему
Аналогичная проблема также возникает при использовании find -exec
. Всегда рекомендуется придерживаться статического кода оболочки, если возможно, а не вставлять расширяемые значения. Это уменьшает риск внедрения нежелательного кода.
# Неправильный способ
var="I want to print '$(beep)'"
sh -c "echo '$var'"
# Правильный способ
var="I want to print '$(beep)'"
sh -c 'echo "$1"' sh "$var"
Рекомендации
Если вы планируете написать статический код в sh -c
, советую оборачивать всю строку в одинарные кавычки. Это поможет избежать многих проблем, связанных с интерпретацией специальных символов. Помните, что наилучший подход — всегда минимизировать динамическое использование кода оболочки, чтобы свести к минимуму риски.