Динамические переменные в оболочке

yash's array builtin имеет некоторые параметры, которые работают только с переменными массива.Пример: параметр -d сообщит об ошибке для переменной, не являющейся массивом:

$ a=123
$ array -d a
array: no such array $a

Таким образом, мы можем сделать что-то вроде этого:

is_array() (
  array -d -- "$1"
) >/dev/null 2>&1

a=(1 2 3)
if is_array a; then
  echo array
fi

b=123
if ! is_array b; then
  echo not array
fi

Этот подход не будет работать, если переменная массива только для чтения . Попытка изменить переменную только для чтения приводит к ошибке:

$ a=()
$ readonly a
$ array -d a
array: $a is read-only

1
01.09.2018, 13:31
3 ответа

В bashвы должны использовать массив для хранения путей, прочитанных пользователем. В общем, лучше хранить отдельные строки (пути )отдельно, чем объединять их в одну строку, которую впоследствии нужно будет правильно проанализировать для извлечения исходных составляющих строк.

#!/bin/bash

echo 'Enter paths, one by one followed by Enter. End input with Ctrl+D' >&2
mypaths=()
while IFS= read -r -p 'Path: ' thepath; do
   mypaths+=( -v "$thepath:/opt/$thepath" )
done

docker run "${mypaths[@]}" fedora

Здесь пользователю предлагается ввести путь несколько раз, пока он не нажмет Ctrl+D . Введенные пути сохраняются в массиве mypaths, который расположен таким образом, что dockerможет использовать его напрямую.

Как только не останется путей для чтения, вызывается команда docker. "${mypaths[@]}"будет расширен до отдельных элементов массива mypaths. Поскольку записи массива хранятся в том виде, в котором они есть (с -vв качестве отдельного элемента перед каждой специально отформатированной строкой pathname:/opt/pathname), это будет правильно интерпретировано оболочкой и docker.Единственными символами, которые не допускаются в именах путей приведенным выше кодом, являются символы новой строки, поскольку они разделяют строки, считываемые с помощью read.

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

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

Связанные:


Для корпусов не-bash(sh):

#!/bin/sh

echo 'Enter paths, one by one followed by Enter. End input with Ctrl+D' >&2
set --
while printf 'Path: ' >&2 && IFS= read -r thepath; do
   set -- "$@" -v "$thepath:/opt/$thepath"
done

docker run "$@" fedora

Здесь мы используем список позиционных параметров вместо массива (, поскольку массивы, отличные от $@, обычно недоступны в sh), но в остальном рабочий процесс идентичен, за исключением явного вывода подсказки. с printf.


Реализовано предложение в конце комментария Stéphane Chazelas , чтобы сценарий брал пути в своей командной строке, а не считывал их из стандартного ввода. Это позволяет пользователю передавать скрипту произвольные пути, даже те, которые readне могут быть легко прочитаны или пользователь не может легко печатать на клавиатуре.

Для bashс использованием массива:

#!/bin/bash

for pathname do
    mypaths+=( -v "$pathname:/opt/$pathname" )
done

docker run "${mypaths[@]}" fedora

Для shс использованием списка позиционных параметров:

#!/bin/sh

for pathname do
    shift
    set -- "$@" -v "$pathname:/opt/$pathname"
done

docker run "$@" fedora

Оба они будут выполняться как

./script.sh path1 path2 path3...
6
27.01.2020, 23:11
#!/bin/bash

echo -n "Enter the Path : "
read path
echo -n "docker run "
echo "${path}" | tr " " "\n" | while read value
do
    echo -n "-v ${value}:/opt/${value} "
done
2
27.01.2020, 23:11

Попробуйте так, имена каталогов не должны содержать пробелов:

#!/bin/bash

echo -n "Enter the Directories, space separated : "
read dirs

docker run $( set -- $dirs; for path; do echo -v $path:/opt/$path/; done ) fedora

Введите что-то вроде foo bar, и он запустится

docker run -v foo:/opt/foo/ -v bar:/opt/bar/ fedora
3
27.01.2020, 23:11

Теги

Похожие вопросы