Использование xargs с pdftk
Я использую следующий код для объединения всех файлов PDF в текущем каталоге:
find . -iname '*.pdf'|sort|xargs|xargs -I {} pdftk {} cat output union.pdf
Первый вызов xargs приводит к преобразованию результатов сортировки в одну строку с элементами, разделенными пробелом. Но результат таков:
Error: Unable to find file.
Error: Failed to open PDF file:
./001.pdf ./002.pdf ./003.pdf ./004.pdf ./007.pdf ./010.pdf ./031.pdf ./057.pdf ./077.pdf ./103.pdf ./131.pdf ./155.pdf ./179.pdf ./205.pdf ./233.pdf ./261.pdf ./285.pdf ./313.pdf ./331.pdf ./357.pdf ./383.pdf ./411.pdf
Errors encountered. No output created.
Done. Input errors, so no output created.
Xargs передает аргумент pdftk с окружающими кавычками? Как это предотвратить? (Пробелы, экранирование и то, как они взаимодействуют с командами, всегда сводят меня с ума...)
1 ответ
Xargs передает аргумент pdftk с окружающими кавычками?
Да и нет, но технически нет. xargs не цитирует, и pdftk также не цитирует.
То, как программы получают аргументы командной строки в Linux/Unix, заключается не в том, чтобы использовать одну строку, которая должна быть заключена в кавычки и не заключена в кавычки - именно так работает пользовательский язык "командной оболочки", а кавычки интерпретируются вашей оболочкой, не самими программами. (Это противоположно тому, как это делает Windows.)
Внутренне программы запускаются с использованием массива (/list/vector) строк, который по своей природе сохраняет точное текстовое содержимое и разделение каждого элемента, поэтому он на самом деле не использует кавычки или экранирование в первую очередь. (То есть - если вам не нужно вкладывать его, в этом случае он возвращается к кавычкам и разбору строк, как вы увидите ниже...)
Например, ваша командная строка анализируется в этом (например, с использованием синтаксиса C-подобного массива, но кавычки на самом деле не являются частью строк):
1. {"find", ".", "-iname", "*.pdf", NULL}
2. {"sort", NULL}
3. {"xargs", NULL}
4. {"xargs", "-I", "{}", "pdftk", "{}", "cat", "output", "union.pdf", NULL}
└─xargs uses these elements as the command─┘
Поэтому, когда xargs читает строку ввода (поскольку -I переводит ее в построчный режим), она заменяет символы {}
в каждом отдельном элементе со строкой ввода, без перестановки элементов в любом случае. Затем он просит ОС запустить результат:
{"pdftk", "./001.pdf ./002.pdf ./003.pdf …", "cat", "output", "union.pdf", NULL}
Таким образом, вам нужен другой способ для достижения этой цели, чем xargs -I
в одиночестве.
Например, вы могли бы попросить xargs запустить оболочку, которая будет интерпретировать / разделять / снимать кавычки так же, как вы ожидаете от оболочки:
find … | sort | xargs | xargs -I {} bash -c "pdftk {} cat output union.pdf"
Элемент, следующий за -c, станет
pdftk ./001.pdf ./002.pdf … cat output union.pdf
и bash разделит его на слова, как и ожидалось. (Но учтите, что поскольку xargs не заключает в кавычки, это разделит имена файлов, которые содержат пробелы, и даст странные результаты, когда имена файлов содержат специальные символы.)Вы можете использовать функцию "подстановки процесса" оболочки:
pdftk $(find … | sort) cat output union.pdf
Это разделит результирующий текст на любой пробел (так же, как
$var
расширение переменной). Линии не должны быть соединены в первую очередь. Но у него будут те же проблемы с именами файлов, содержащими пробелы, и немного меньше проблем со специальными символами.Рекомендовано: Вы можете полностью избежать "find" и "xargs" и напрямую использовать встроенный шаблон подстановки в интерактивной оболочке:
pdftk *.pdf cat output union.pdf
Обычный * не рекурсивный, но в Bash или zsh у вас также есть **, который является рекурсивным режимом:
shopt -s globstar # enable the feature (only needed in bash) pdftk **/*.pdf cat output union.pdf
(Результаты сопоставления всегда будут отсортированы, по крайней мере, в оболочках, использующих язык POSIX sh. И поскольку оболочка напрямую расширяет каждое имя файла до отдельного элемента командной строки, проблем с цитированием вообще не будет, даже с необычными именами файлов.)