преобразование списка путей к файлам из одной команды в аргументы командной строки для другой

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

Я хочу передать список путей к файлам, полученный одной командой (программа KDE baloosearch6), в качестве аргументов командной строки другой команде (программа для просмотра изображений KDE gwenview). Вечная проблема в том, что пути к изображениям содержат пробелы, поэтому мне нужно заключать их в кавычки, например: gwenview '/path/to/my first file' '/path to/another file'. У программ нет опций -print0/-0.

Claude AI дал мне однострочное решение для этого:

baloosearch6 foobar | sed "s/.*/'&'/" | tr '\n' ' ' \
   | xargs gwenview

но должен быть лучший способ, например, обработка массива путей к файлам. Как правильно собрать массив строк в zsh дает магию zsh для сбора строк вывода в массив:

files=("${(@f)$(baloosearch6 foobar)}")

но теперь мне нужно превратить массив путей к файлам обратно в заключенные в кавычки параметры командной строки. Я прочитал главу о подстановках из “Руководства пользователя по Z-Shell” и не нашел ответа. Возможно, мне стоит поработать с IFS и подобными вещами.

В идеале я хочу одну команду/функцию make_cli_args, которая превращает вывод одной команды в заключенные в кавычки аргументы для другой. (Почему это так сложно?)

Если у вас есть массив и вы просто хотите передать его элементы в неизменном виде команде, достаточно использовать $array в zsh или "${array[@]}" (в кавычках!) в Bash/ksh. Первый вариант пропускает пустые элементы, однако "${array[@]}" работает в любом случае:

zsh:

% l=("foo * bar" '' $'new\nline')
% printf '<%s>\n' $l
<foo * bar>
<new
line>
% printf '<%s>\n' "${l[@]}"
<foo * bar>
<>
<new
line>

Bash:

$ l=("foo * bar" '' $'new\nline')
$ printf '<%s>\n' "${l[@]}"
<foo * bar>
<>
<new
line>

С другой стороны, если вам нужно передать элементы массива в оболочку для выполнения в качестве командной строки, вам нужно будет правильно заключить их в кавычки. Это может произойти, если вам нужно встроить значения в сценарий оболочки, или запустить их с помощью eval (по какой-либо причине), или передать их через ssh.

Здесь вы можете использовать printf %q в Bash или zsh, или модификатор (q) в zsh.

Функция printf %q в Bash обычно использует обратные слеши, так что вывод выглядит некрасиво, но это должно работать (команда echo добавляет перенос строки в конце):

$ printf '%q ' "${l[@]}"; echo
foo\ \*\ bar '' $'new\nline'

Функция (qqqq) в zsh использует кавычки $'...', что выглядит лучше:

% printf '%q ' "${l[@]}"; echo
foo\ \*\ bar '' new$'\n'line

% printf "%s " ${(qqqq)l}; echo
$'foo * bar' $'' $'new\nline'

В любом случае я бы постарался избегать вставки данных в кавычках внутрь кода. Обычно это вызывает сложности и имеет множество крайних случаев.

В большинстве случаев с именами файлов достаточно передать их в сыром виде, по одному на строку, и считать их в массив. (И убедитесь, что в именах файлов нет переносов строк, или проверьте их и сообщите об этом.)

Ответ или решение

Для решения задачи передачи списка пути к файлам, полученного от одной команды (в данном случае программы KDE baloosearch6), в качестве аргументов командной строки для другой команды (это программа просмотра изображений KDE gwenview), необходимо учитывать, что пути к файлам могут содержать пробелы. Это усложняет процесс, так как необходимо правильно обрабатывать эти пробелы, чтобы передать корректные аргументы.

Подход с использованием массива в Zsh

Одним из эффективных способов решения данной задачи в Zsh является использование массивов для хранения путей к файлам, а затем их преобразование в соответствующий формат для передачи как аргументы другой команде.

1. Сбор путей в массив

Первым шагом является сбор путей к файлам в массив. Это можно сделать, используя следующий синтаксис:

files=("${(@f)$(baloosearch6 foobar)}")
  • baloosearch6 foobar — это команда, которая возвращает список путей к файлам.
  • @f — это расширение, которое обрабатывает выходные данные как строки, разбивая их по символу новой строки.

2. Преобразование массива в строку с аргументами

Следующим шагом будет преобразование массива путей к файлам в строки, которые могут быть переданы как аргументы команде gwenview. Удобным способом является использование модификатора (qqqq) для автоматического экранирования строк:

gwenview $files

Если требуется более явная обработка для создания строк, которые можно использовать и в других контекстах, можно использовать команду printf с форматом:

gwenview $(printf "%q " "${files[@]}")

Тем не менее, использование $(printf "%q ") может привести к не самым читаемым результатам, так как эта команда экранирует специальные символы.

3. Реализация функции make_cli_args

Чтобы создать единую функцию make_cli_args, которая будет обрабатывать команды, можно использовать следующий код:

make_cli_args() {
  local files=("${(@f)$(baloosearch6 $1)}")
  gwenview "${files[@]}"
}
  • Эта функция принимает один аргумент (например, foobar для поиска) и передаёт полученные пути в gwenview.

Альтернативный подход в Bash

Если вы работаете в Bash, аналогичный процесс может быть реализован с использованием синтаксиса массива и printf:

make_cli_args() {
  local files=($(baloosearch6 "$1"))
  gwenview "${files[@]}"
}

Однако обратите внимание, что в Bash для обработки пробелов в именах файлов необходимо использовать следующий подход, чтобы избежать искажений:

printf '%q ' "${files[@]}"

Заключение

В результате, грамотно построенная функция make_cli_args позволяет изящно и эффективно передавать пути к файлам в другую команду, минимизируя возможные ошибки, связанные с неправильной интерпретацией пробелов и спецсимволов. Этот подход высокоэффективен и может быть адаптирован под другие команды и сценарии в зависимости от ваших потребностей.

Оцените материал
Добавить комментарий

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