Расширение параметров массива Bash в пределах имени параметра: неправильная замена - PullRequest
1 голос
/ 22 августа 2011

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

Хотя я могу объявить массив Directories0 (входные каталоги) для запуска, я никак не могу сослаться на него ... результат: неправильная замена. Очевидно, Directories1 будет глубина = 1, Directories2 это глубина = 2, и так далее ...

Ниже приведен фрагмент кода:

let Recurse=4               ## say... variable value ##
[ "$Recurse" ] && let MaxDepth="$Recurse" || let MaxDepth=0
declare -i depth=0

IFS=$'\n'
## declare -a Directories${depth}=("${@}")      ##  <—— doesn't work
## declare -a Directories${depth}="("${@}")"        ##  <—— works if the brackets only are quoted...
## declare -a Directories${depth}=\("${@}"\)        ##  <—— ... or escaped
declare -a "Directories${depth}=("${@}")"
IFS=$' \t\n'

## Nested loop, depth counter increases for each directory depth. I want to stop at a specific depth which is entered as an option ##
for (( depth = 0; depth <= MaxDepth; depth++ )); do                  ## MaxDepth is entered as option ##
    until [ -z "${Directories${depth}[*]}" ]; do                       ## ***** bad substitution error ***** ##
        declare input="$(follow "${Directories${depth}[0]}")"       ## follow is a script that resolves symlinks and Finder aliases ##
        CheckDirectory "${input%/}/"                                  ## check directory ##
        case $? in

        ## Tests passed ##
        0)  if [[ "$Recurse" && "$depth" -lt "$MaxDepth" ]]; then
                IFS=$'\n'
                ## get ready to check sub-directories ##
                declare -a Directories$(( depth + 1 ))="("${Directories$(( depth + 1 ))[@]}" $(find -P "${Directories${depth}[0]}" -type d -mindepth 1 -maxdepth 1 -exec follow '{}' \;))"
                IFS=$' \t\n'
            fi
            true;;

        ## Tests failed ##
        *)  false;;
        esac
        [ $? -eq 0 ] && unset Directories${depth}[0] || exit 1             ## if test fails, exit, if succeeds, move on to next directory ##
        declare -a Directories${depth}="("${Directories${depth}[@]}")"      ## re-shuffle the array to get rid of null value at index 0 ##
        (( element++ ))
    done
done

Ниже приведена упрощенная версия на тот случай, если вы не хотите проходить код выше, в этом суть проблемы:

depth=2
declare -a "Directories${depth}=(yo man ma me mo)"
echo "${Directories${depth}[4]}"
    > -bash: ${Directories${depth}[4]}: bad substitution
echo "${Directories2[4]}"
    > mo

Решения, кто-нибудь?

- Aesthir

Ответы [ 5 ]

4 голосов
/ 22 августа 2011

Вам нужно буквальное имя переменной внутри конструкции ${}.Если вы хотите сослаться на переменную, имя которой определяется во время выполнения, вам необходимо явно пройти уровень косвенности.

name="Directories${depth}[4]"
echo ${!name}

Это не помогает для назначений.Пока вы назначаете в текущей области (а не в охватывающей области), вы можете выполнять вычисляемые назначения с помощью встроенного typeset.Однако, будьте осторожны: bash имеет эвристику, которая позволяет преобразовывать назначения, похожие на синтаксис назначения массива, в назначения массива.Это означает, что приведенный ниже код в порядке, если элемент собирается хранить абсолютное имя файла (которое всегда начинается с /), но не если он собирается хранить произвольное имя файла (которое может быть чем-то вроде (foo)).

typeset "Directories${depth}[4]=new value"

В качестве альтернативы возможно выполнение присваивания переменной, имя которой определяется во время выполнения с помощью любой оболочки с помощью eval.Это имеет преимущество работы в любой оболочке, а не только в bash.Вы должны быть очень осторожны, хотя: трудно правильно процитировать цитату.Лучше всего, чтобы аргумент eval делал как можно меньше.Используйте временную переменную для получения значения.

eval "tmp=\${Directories${depth}[4]}"
echo "$tmp"
tmp="new value"
eval "Directories${depth}[4]=\$tmp"
1 голос
/ 22 августа 2011

Используйте eval, это работает:

eval echo \${Directories${depth}[4]}

mo
0 голосов
/ 29 мая 2012

Возможно, это (запоздалый) ответ на вопрос?(Вопрос не так ясен, как верит спрашивающий, я чувствую…)другой переменной, как можно присвоить ей значение.Например, если установка «foobar = value» является нормальным способом установить значение переменной, то что, если x = foo и y = bar, как установить «$ {x} $ {y} = value» =>Например:

  foobar=test
  echo $foobar
  > test

  x=foo
  y=bar
  eval "export ${x}${y}=\"value\""
  echo $foobar
  > value

Если я неправильно понял вопрос, ну, я не сильно удивлен: -)

0 голосов
/ 10 декабря 2011

Вы были почти там:

declare -a Directories${depth}="( yo man ma me mo )"

работает (и без eval, кстати).Чтобы получить доступ к значению, используйте синтаксис $ {!}:

temp=Directories$depth[@]
echo ${!temp}
0 голосов
/ 22 августа 2011

попробуй eval

&#35;!/bin/bash
depth=2
declare -a "Directories${depth}=(yo man ma me mo)"
eval echo "\${Directories${depth}[4]}"
echo "${Directories2[4]}"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...