Вопрос или проблема
Я свожу вопрос к (как я полагаю) самому простому случаю. Допустим, у меня есть скрипт myscript.sh
со следующим содержанием:
#!/bin/bash
IFS='%20'
echo "$*"
Если я запущу команду следующим образом, вывод будет выглядеть так:
me@myhost ~ $ ./myscript.sh fee fi fo fum
fee%fi%fo%fum
Это ожидаемое поведение, как описано в руководстве по bash
:
* Расширяется до позиционных параметров, начиная с первого. Когда
расширение происходит внутри двойных кавычек, оно расширяется в одно
слово, где значение каждого параметра разделено первым
символом специальной переменной IFS. То есть, "$*" эквивалентно
"$1c$2c...", где c — это первый символ значения
переменной IFS. Если IFS не установлен, параметры разделяются
пробелами. Если IFS пуст, параметры соединяются
без промежуточных разделителей.
Тем не менее, я бы хотел получить следующий вывод:
fee%20fi%20fo%20fum
Таким образом, используя разделитель из нескольких символов, а не один символ.
Существует ли способ сделать это, который был бы родным для bash
?
ОБНОВЛЕНИЕ:
Основываясь на данных от mikeserv ниже, и на описании по Почему printf лучше, чем echo?, я в итоге сделал следующее (опять же, сведен к самому простому случаю, как в приведенном выше примере):
#!/bin/bash
word="$1"
shift
if [ "$#" -gt 0 ] ; then
word="$word$(printf '%%20%s' "$@")"
fi
printf '%s\n' "$word"
unset word
printf
применяет свою строку формата к каждому аргументу, который следует за ним в выводе. Это встроенная команда bash
и может быть использована для применения строки-разделителя к списку аргументов – своего рода.
Например:
printf %s:delimit: arg1 arg2 arg3
arg1:delimit:arg2:delimit:arg3:delimit:
Дело в том, что printf
не остановливается в применении своей строки формата в конце своих аргументов, и поэтому последний получает добавленный разделитель. Это можно решить в некоторых случаях:
printf %b:delimit: \\0150 \\0145 \\0171\\c
h:delimit:e:delimit:y
printf
интерпретирует C и восьмеричные экраны как %b
айты с определенным форматом, и также с форматом %b
вы \c
режете его вывод в определенной точке, что и объясняет, почему printf
не продолжает символ y выше с :delimit:
строкой, как это было бы указано в его строке формата.
Так что, если вы хотите, чтобы каждый аргумент интерпретировался буквально и без завершающего разделителя, тогда вам нужно решить проблему внутри самого списка аргументов:
set -- arg1 arg2 arg3
for arg do shift
set -- "$@" :delimit: "$arg"
done; shift
printf %s "$@"
arg1:delimit:arg2:delimit:arg3
В zsh
вы можете использовать j:string:
флаг расширения параметров:
set -- fee fi fo fum
delims=%20
print -rl ${(j:$delims:)@}
Если аргументы (как в исходном примере) не имеют внутренних пробелов, то
- преобразуйте их в одну строку, разделенную пробелами, поскольку пробел является 1-м символом значения по умолчанию
IFS
- замените каждый пробел в этой строке на
%20
x="$*"
echo ${x// /%20}
Если аргументы могут иметь внутренние пробелы, то
- установите
IFS
в null (пустую строку) - преобразуйте аргументы в одну строку, добавляя перед каждым
%20
; результат будет без разделителей, потому чтоIFS
пуст - пропустите первые 3 символа результата, чтобы пройти мимо начального
%20
IFS=
x=${*/#/%20}
echo ${x:3}
(Проверено с Bash 5.2.32)
Если вы используете bash
(или другой шелл с разрешенным расширением переменных) и аргументы не имеют пробелов внутри, вы можете сделать:
#!/bin/bash
line=$*
echo "${line// /:delimiter:}"
В случае наличия пробелов вам придется использовать другой разделитель, установив переменную IFS
перед line
.
Ответ или решение
При использовании Bash для обработки строк и передачи аргументов сценария, может возникнуть необходимость в установке многократного разделителя. Хотя переменная IFS (Internal Field Separator) позволяет определять разделители, она может работать только с одиночными символами. В этой статье мы рассмотрим, как создать скрипт на Bash, который использует многосимвольный разделитель, например, "%20", и избежать сложностей, связанных с пробелами в аргументах.
Шаги к решению проблемы
-
Инициализация сценария:
Начнем с создания простого сценария. Объявим переменную для хранения первого аргумента и подготовим основную логику для обработки остальных аргументов. -
Обработка аргументов:
Если аргументов несколько, мы будем объединять их, добавляя разделитель между каждым из них. Используяprintf
, мы сможем контролировать формат вывода так, чтобы избежать добавления лишнего разделителя в конце. -
Финальная проверка:
Убедимся, что разделитель не добавляется после последнего аргумента, и выведем результат.
Пример скрипта
Вот пример сценария, который реализует вышеперечисленные шаги:
#!/bin/bash
# Проверяем, что передан хотя бы один аргумент
if [ "$#" -eq 0 ]; then
echo "Необходимо указать хотя бы один аргумент."
exit 1
fi
# Начинаем с первого аргумента
result="$1"
# Используем цикл для обработки остальных аргументов
shift # Убираем первый аргумент из списка
for arg in "$@"; do
result="$result%20$arg" # Добавляем разделитель и текущий аргумент
done
# Выводим результат
echo "$result"
Подробное объяснение
-
Инициализация: Сначала мы проверяем, был ли передан хотя бы один аргумент. Если нет, выводим сообщение об ошибке и выходим из скрипта.
-
Считывание аргументов: Мы сохраняем первый аргумент в переменной
result
, а затем с помощью командыshift
передвигаем позиционные параметры, чтобы остаток аргументов можно было обрабатывать в циклеfor
. -
Добавление разделителя: При каждой итерации цикла к результату добавляется разделитель
%20
, который следует перед текущим аргументом. Это обеспечивает нужный нам формат. -
Вывод: Наконец, мы выводим окончательную строку.
Приемы обработки пробелов
Если аргументы могут содержать пробелы, используйте конструкции вокруг обработки аргументов. Например, вы можете заменить shift
и for
на другой подход, чтобы избежать проблем с пробелами:
IFS= # Устанавливаем IFS в пустую строку
result="${*// /%20}" # Заменяем все пробелы в строке
echo "$result"
Заключение
Bash предоставляет мощные инструменты для обработки строк и аргументов, но при работе с разделителями необходимо учитывать ограничения, связанные с IFS. Обсужденный метод позволяет эффективно объединять аргументы с использованием множественных символов, таких как "%20", что является особенно полезным в сценариях, требующих передачи данных через URL или обработки запросов. Используйте предложенные техники, чтобы избежать распространенных ошибок и создайте надежные скрипты.