Я пытаюсь написать bash-скрипт для вывода значений трех переменных. Продолжайте получать ошибки. Что я делаю не так?
INPUT1=/tmp/dir1
INPUT2=/tmp/dir2
INPUT3=/tmp/dir3
for i in 1 2 3
do
echo $(INPUT$i)
done
Когда я запускаю этот скрипт, вывод будет:
syntax error: operand expected (error token is "/tmp/dir1
Bash не поддерживает такой синтаксис напрямую. Вы можете использовать 'eval', однако в современных версиях 'bash' самый простой способ - использовать массив
input=( "/tmp/dir1" "/tmp/dir2" "/tmp/dir3" )
for i in {0,1,2}; do echo "${input[i]}"; done
(обратите внимание, что массивы bash имеют нулевую индексацию); или перечислить все элементы, которые вы можете использовать
for i in "${input[@]}"; do echo "$i"; done
Лучший способ состоит в том, чтобы определить INPUT
переменная как массив:
#!/bin/bash
INPUT[1]=/tmp/dir1
INPUT[2]=/tmp/dir2
INPUT[3]=/tmp/dir3
for i in "${INPUT[@]}"
do
echo $i #or $INPUT[$i]
done
См. также: http://www.cyberciti.biz/faq/bash-for-loop-array/
Я согласен, что использование массива, как показано в других ответах, является лучшим способом решения реальной задачи ОП.
Но эти ответы не дают прямого ответа на поставленный вопрос, поэтому я сделаю это:
Вот как вы можете сделать это с помощью eval
для доступа к переменным, точно следуя тому, как ОП дал его код:
INPUT1=/tmp/dir1
INPUT2=/tmp/dir2
INPUT3=/tmp/dir3
for i in {1..3} ; do
eval "echo \$INPUT$i"
done
Выход:
/tmp/dir1
/tmp/dir2
/tmp/dir3
bash
на самом деле поддержки что-то достаточно близкое к тому, что Вы сначала попробовали.
INPUT1=/tmp/dir1
INPUT2=/tmp/dir2
INPUT3=/tmp/dir3
for s in INPUT{1..3}; do
echo ${!s}
done
Это работает потому что:
INPUT{1..3}
расширяется до INPUT1 INPUT2 INPUT3
. (Это эквивалентно INPUT{1,2,3}
.)
Где $s
расширение s
, ${!s}
расширение расширения s
.
Например, в первом повторении цикла, если $s
появившийся это было бы расширено до INPUT1
. Если $INPUT1
появившийся это было бы расширено до /tmp/dir1
. Таким образом ${!s}
расширен до /tmp/dir1
.
Этот вид расширения параметра называют косвенным расширением. В этой ситуации и многим нравится он, встроенное !
синтаксис достигает этого более сжато и изящно, чем eval
.
Для получения дополнительной информации посмотрите, что действительно ли возможно создать имена переменной из других переменных в ударе? на Переполнении стека.
Некоторые протесты (которые также относятся к некоторым из других ответов, которые были отправлены):
Если любые значения INPUTn
может содержать whitespace1 (как spaces2, вкладки или новые строки) или конструкции со специальными символами, которые были бы расширены (такой как $varname
), заключение в кавычки должно использоваться.
Одинарные кавычки для присвоения являются лучшими, если Вы хотите предотвратить все расширение там:
INPUT1=/tmp/dir1
→ INPUT1='/tmp/dir with spaces'
(Только a '
символ кавычки рассматривают особенно, поскольку он будет действовать как заключительная метка кавычки.)
Но используйте двойные кавычки, чтобы все еще позволить переменной быть расширенной:
echo ${!s}
→ echo "${!s}"
(Это работает, даже если значение после расширения содержит a $
. Расширенное содержание не расширено снова.
Например, BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO"
печать BAR $BAZ
, нет BAR QUUX
.)
Если любой из INPUTn
мог бы взять значение это echo
интерпретировал бы как опция вместо текста, чтобы распечатать, заменить echo
с printf '%s\n'
.3
В настоящее время это -
сопровождаемый еще одним e
, E
, или n
. Но необходимо, вероятно, рассмотреть что-либо запускающееся с -
как опасный в случае, если будущие версии удара добавляют новые опции.
Хотя с точки зрения синтаксиса косвенное расширение, столь же представленное выше, почти так же просто как выполнение его с массивом, все еще можно хотеть использовать массив потому что:
Возможно, Вы предназначили /tmp/dir1
, /tmp/dir2
, и /tmp/dir3
как непрозрачные примеры, которые могут быть заменены чем-либо.
Но если Вы действительно хотите сделать массив тех определенных значений, я рекомендую:
input=(/tmp/dir{1..3})
Точно так же, если Ваша цель состоит в том, чтобы только циклично выполниться через /tmp/dir1
, /tmp/dir2
, и /tmp/dir3
и выполните некоторое действие с каждым, затем Вы не должны хранить их ни в каком виде переменной:
for s in /tmp/dir{1..3}; do
echo $s
done
Если значения s
будет содержать пробельные или специальные символы (см. выше), заключите им в кавычки, но уезжайте {...}
диапазон закрыл кавычки путем отдельного заключения в кавычки текста налево и права на него - он не будет расширен, если заключено в кавычки, даже в двойных кавычках.
for s in '/tmp/dir '{1..3}' with spaces'; do
echo "$s"
done
Это печатает:
/tmp/dir 1 with spaces
/tmp/dir 2 with spaces
/tmp/dir 3 with spaces
И если Ваша цель состоит в том, чтобы действительно только распечатать /tmp/dir1
, /tmp/dir2
, и /tmp/dir3
, каждый на его собственной строке, затем этот единственный command3 достаточен:
printf '%s\n' /tmp/dir{1..3}
1: Оболочка разделяет текст на слова на пробеле или, если IFS
переменная определяется на разделителях полей, которых она указывает. Но Вы обычно не должны волноваться о IFS
если Вы не установили его сами.
2: echo hello world
на самом деле работает: это печатает hello world
, точно так же, как echo 'hello world'
. Это вызвано тем, что echo
печатает одиночный пробел между каждым отраженным аргументом. Однако echo hello world
не эквивалентно echo 'hello world'
: первый все еще печатает hello world
в то время как последняя печать hello world
.
3: Для получения дополнительной информации о bash
printf
встроенный, посмотрите вывод help printf
или этот раздел руководства Bash. printf
также исполняемый файл, который можно назвать от других оболочек (и в той способности стандартизирован).