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