Как динамически использовать переменную внутри цикла while

В ksh93 и bash:

$ pwd
/tmp
$ mkdir test_dir
$ mv test_dir another_name
$ cd $_
$ pwd
/tmp/another_name

$_ заменяется на последний аргумент предыдущей команды.

В качестве функции оболочки:

mvcd () {
    mv -- "$1" "$2"
    cd -P -- "$_"
}

Но вы также можете использовать

mvcd () {
    mv -- "$1" "$2" &&
    cd -P -- "$2"
}

, так как это позаботится о том, чтобы не пытаться изменить каталог, если mv не удалось.

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

Команда -P с cd необходима для того, чтобы cd интерпретировала пути так же, как mv («физически» а не "логически"). Это позволяет избежать путаницы, когда новое местоположение указано с путем, который содержит .. и пересекает символические ссылки.

Если вы перемещаете, а не просто переименовываете каталог, нужно будет разобраться со случаем, когда перемещение не связано с переименованием каталога:

mv some_dir existing_dir

Это приведет к перемещению some_dir в существующий_каталог, поэтому нужно

cd existing_dir/some_dir

изменить рабочий каталог на перемещенный каталог впоследствии.

Об этом позаботится следующая модифицированная функция оболочки:

mvcd () {
    if [ -d "$2" ]; then
        mv -- "$1" "$2" &&
        cd -P -- "$2/$1"
    else
        mv -- "$1" "$2" &&
        cd -P -- "$2"
    fi
}

или "короче":

mvcd () {
    if [ -d "$2" ] && mv -- "$1" "$2"; then
        cd -P -- "$2/$1"
    elif mv -- "$1" "$2"; then
        cd -P -- "$2"
    fi
}

Невозможно объединить mv и cd в действительно атомарный операция. Сначала должен произойти mv, затем cd, как бы вы на это ни смотрели, даже если вы написали это на C. Делая одно за другим (проверяя статус выхода of mv) — правильный способ сделать это.

0
14.09.2017, 08:18
2 ответа

Это намного проще с петлей for:

for i in 1 2 3; do
  echo var$i\ =\ hello$i
done

Проверено в bash.

0
28.01.2020, 02:24

В POSIX shвам потребуется evalдля использования переменных с динамическими именами.

i=0
while [ "$i" -le 3 ]
do
   eval '
     var'"$i"'="hello${i}"
     echo "var$i = ${var'"$i"'}"
   '
   i=$((i+1))
done
echo "${var1}"

При использовании evalочень важно убедиться, что в аргументе, переданном в eval(, раскрываются только те переменные, которые необходимо расширить, и что их значение очищается (здесь мы знаем, что это безопасные последовательности десятичные цифры )).

Выше только два $iрасширены. Для этого мы выходим из одинарных (сильных )кавычек и вставляем $iвнутри двойных кавычек:eval '...'"$i"'...'.

Так как очень трудно получить правильное цитирование (и опасно, если вы не ), лучше максимально ограничить использование eval. В идеале только передать содержимое динамической переменной в статическую и/или обратно как:

i=0
while [ "$i" -le 3 ]
do
   var=hello$i # $var with static name
   eval "var$i=\$var" # transfer into variable with dynamic name

   echo "var$i = $var" # use var with static name instead of dynamic one
                       # everywhere else (for which we don't need eval)

   i=$((i+1))
done
echo "${var2}"

В ksh/zsh/bash/yashвы можете использовать массивы вместо (или ассоциативные массивы в ksh93, zshили последние версии bash). Обратите внимание, что индексы массивов ksh/bashначинаются с 0, а массивы являются разреженными (, больше похожими на ассоциативные массивы с ключами, ограниченными положительными целыми числами ), в то время как во всех других оболочках (, включая zshи yashна фронте типа Bourne -), индексы начинаются с 1, а массивы являются обычными массивами.

В ksh/ bash/zsh -o ksharrays:

unset -v var
i=0; while [ "$i" -le 3 ]; do
  var[i]=hello$i
  echo "var[$i]=${var[i]}"
  i=$((i+1))
done
echo "${var[1]}"

Или используя другое ((...))расширение синтаксиса POSIX sh, общее для ksh/ zsh/bash:

unset -v var
i=0; while ((i <= 3)); do
  var[i]=hello$i
  echo "var[$i]=${var[i]}"
  ((++i))
done
echo "${var[2]}"

Или с ksh93, bash,zsh -o ksharrays:

unset -v var
for ((i = 0; i <= 3; i++)); do
  var[i]=hello$i
  echo "var[$i]=${var[i]}"
done
echo "${var[3]}"
4
28.01.2020, 02:24

Теги

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