Передайте несколько файлов как единый параметр

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

Я пытаюсь создать оболочку для выполнения инструмента несколько раз, а затем объединить некоторые из результатов. Я хотел бы передать два набора файлов в мой скрипт-оболочку, а затем запустить инструмент для каждой пары файлов. Я хотел бы, чтобы это работало так:

multitool.sh -a a*.txt -b b*.txt (расширяя подстановочные знаки для соответствия всем доступным файлам)

Затем внутри multitool.sh, я запускаю инструмент на a1.txt b1.txt, a2.txt b1.txt, a1.txt b2.txt, a2.txt b2.txt и так далее, с переменным количеством файлов a и b.

Я следовал этому руководству, объясняющему основы обработки опций, и я могу использовать getops для обработки -h, но ничего больше.

Вот где я сейчас:

#!/bin/bash

while getopts ":hpg:" option; do
    case $option in 
        h) # показать помощь
            echo "-h для вывода этой помощи и выхода, -p для файлов пиков, -g для файлов bedgraph."
            exit;;
        p) # список файлов пиков для обработки
            l_peaks=$OPTARG;;
        g) # список файлов bedgraph для обработки
            l_bgraphs=$OPTARG;;
        \?) # Неверная опция
            echo "Ошибка! Неверная опция!"
            echo "-h для вывода этой помощи и выхода, -p для файлов пиков, -g для файлов bedgraph."
            exit;;
    esac
done

echo "$l_peaks"
echo "$l_bgraphs"

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

Как я могу передать эти списки файлов в качестве одной опции каждый?

Нет, не объединяйте несколько имен файлов в одну строку. Во-первых, потому что оболочки этого не поддерживают(*), но также потому что в общем случае, имена файлов могут быть любыми строками. Нет символа или последовательности символов, которую вы могли бы использовать в качестве разделителя, которая не была бы допустимой в имени файла. За исключением нулевого байта, но вы не можете передать его в качестве аргумента командной строки.

Поэтому просто заполните два массива именами файлов, которые вы получаете в качестве аргументов. Оттуда также просто перебирать списки.

Хотя getopts остановится, когда увидит необязательный аргумент, поэтому нам придется использовать какую-то строку-разделитель. (Которая, опять же, должна быть чем-то, что может быть именем файла, но, по крайней мере, она должна будет соответствовать полному имени файла, чтобы что-то пошло не так. Давайте выберем ::, и если у вас когда-либо будет такое имя файла, убедитесь, что передадите его как ./::.)

#!/bin/bash
list=a
lista=()
listb=()
while [ "$1" ]; do
    case $1 in 
        ::) list=b;;
        *) if [ "$list" = a ]; then lista+=("$1")
           else                     listb+=("$1")
           fi;;
    esac
    shift
done

printf "файлы в списке a:\n"
printf " <%s>\n" "${lista[@]}"
printf "\n"
printf "файлы в списке b:\n"
printf " <%s>\n" "${listb[@]}"
printf "\n"
printf "комбинации:\n"
for b in "${listb[@]}"; do
    for a in "${lista[@]}"; do
        printf "<%s> <%s>\n" "$a" "$b"
    done
done

запуск этого дает что-то вроде этого:

$ bash lists.sh a*.txt :: b*.txt
файлы в списке a:
 <a1.txt>
 <a2.txt>
 <a3.txt>

файлы в списке b:
 <b1.txt>
 <b2.txt>

комбинации:
<a1.txt> <b1.txt>
<a2.txt> <b1.txt>
<a3.txt> <b1.txt>
<a1.txt> <b2.txt>
<a2.txt> <b2.txt>
<a3.txt> <b2.txt>

Выполните getopts перед тем циклом, если это необходимо.

(* С возможным исключением zsh.)

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

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

Теория

Когда мы говорим о передаче списков файлов как единой опции в Bash-скриптах, у нас возникает несколько проблем. Во-первых, оболочка (shell) Bash не поддерживает передачу файловых имен как единой строки, разделенной каким-либо специальным символом, например, пробелом. Это связано с тем, что имена файлов могут содержать практически любые символы, включая пробелы и специальные символы, что затрудняет использование стандартных разделителей. Исключение составляет только нулевой байт (NUL), но его невозможно использовать в командной строке.

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

Пример решения

Рассмотрим, как можно обойти вышеупомянутые ограничения и организовать более гибкую систему обработки списков файлов. В данном случае, можно использовать два массива для хранения списков файлов, и специальный разделитель, например "::", чтобы разделять списки.

Вот пример скрипта, который реализует данное решение:

#!/bin/bash
list=a
lista=()
listb=()
while [ "$1" ]; do
    case $1 in 
        ::) list=b;; # Переключаемся на другой список
        *) if [ "$list" = a ]; then 
               lista+=("$1") # Добавляем файл в первый список
           else                     
               listb+=("$1") # Добавляем файл во второй список
           fi;;
    esac
    shift
done

printf "Файлы в списке a:\n"
for file in "${lista[@]}"; do
    printf " <%s>\n" "$file"
done
printf "\nФайлы в списке b:\n"
for file in "${listb[@]}"; do
    printf " <%s>\n" "$file"
done

printf "\nКомбинации:\n"
for b in "${listb[@]}"; do
    for a in "${lista[@]}"; do
        printf "<%s> <%s>\n" "$a" "$b"
    done
done

Применение

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

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

Заключение

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

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

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