Как мне использовать многосимвольный разделитель для расширения массива в bash?

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

Я свожу вопрос к (как я полагаю) самому простому случаю. Допустим, у меня есть скрипт 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. преобразуйте их в одну строку, разделенную пробелами, поскольку пробел является 1-м символом значения по умолчанию IFS
  2. замените каждый пробел в этой строке на %20
x="$*"
echo ${x// /%20}

Если аргументы могут иметь внутренние пробелы, то

  1. установите IFS в null (пустую строку)
  2. преобразуйте аргументы в одну строку, добавляя перед каждым %20; результат будет без разделителей, потому что IFS пуст
  3. пропустите первые 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", и избежать сложностей, связанных с пробелами в аргументах.

Шаги к решению проблемы

  1. Инициализация сценария:
    Начнем с создания простого сценария. Объявим переменную для хранения первого аргумента и подготовим основную логику для обработки остальных аргументов.

  2. Обработка аргументов:
    Если аргументов несколько, мы будем объединять их, добавляя разделитель между каждым из них. Используя printf, мы сможем контролировать формат вывода так, чтобы избежать добавления лишнего разделителя в конце.

  3. Финальная проверка:
    Убедимся, что разделитель не добавляется после последнего аргумента, и выведем результат.

Пример скрипта

Вот пример сценария, который реализует вышеперечисленные шаги:

#!/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 или обработки запросов. Используйте предложенные техники, чтобы избежать распространенных ошибок и создайте надежные скрипты.

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

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