Я пытаюсь написать 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
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}
- расширение расширения из [ 1113].
Например, на первой итерации цикла, если бы появилось $s
, оно было бы расширено до INPUT1
. Если появится $INPUT1
, он будет расширен до /tmp/dir1
. Таким образом, ${!s}
расширен до /tmp/dir1
.
Этот вид расширения параметра называется косвенным расширением . В этой ситуации и многим нравится, встроенный синтаксис !
достигает этого более компактно и элегантно, чем eval
.
Для получения дополнительной информации см. Можно ли создавать имена переменных из других переменных в bash? в Переполнение стека . sup>
Некоторые предупреждения (которые также относятся к некоторым другим опубликованным ответам):
Если какие-либо значения INPUTn
могут содержит пробелы 1 sup> (подобно пробелам 2 sup>, табуляции или [ 1173] должны использоваться конструкции новой строки ) или со специальными символами, которые будут расширены (например, $varname
), с цитированием .
Одиночные кавычки для назначения лучше всего подходят, если вы хотите предотвратить все раскрытие в них:
INPUT1=/tmp/dir1
→ INPUT1='/tmp/dir with spaces'
(Только символ кавычки '
обрабатываются специально, так как это будет действовать как закрывающая кавычка.) sup>
Но используйте двойные кавычки, чтобы разрешить расширение переменной:
echo ${!s}
→ echo "${!s}"
(Это работает, даже если значение после расширения содержит $
. Расширенное содержимое больше не раскрывается.
Например, BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO"
печатает BAR $BAZ
, [ 1188] not BAR QUUX
.) Sup>
Если любой из INPUTn
может принимать значение, которое echo
будет интерпретироваться как опция вместо текста для печати замените echo
на printf '%s\n'
. 3 sup>
В настоящее время это -
, за которым следует еще один e
], E
или n
. Но вы, вероятно, должны считать что-либо, начинающееся с -
, опасным, если в будущих версиях bash появятся новые опции.
Хотя, с точки зрения синтаксиса, косвенное расширение, представленное выше, почти так же просто, как делать это с массивом, , вы все равно можете использовать массив , потому что: [11110 ]
Возможно, вы предполагали /tmp/dir1
, /tmp/dir2
и /tmp/dir3
в качестве непрозрачных примеров, которые могут быть заменены чем-либо.
Но если вы действительно хотите создать массив из этих конкретных значений, я рекомендую:
input=(/tmp/dir{1..3})
Аналогично, если ваша цель - просто просмотреть циклы /tmp/dir1
, /tmp/dir2
и [1147 ] и выполните некоторые действия с каждым, тогда вам не нужно хранить их в какой-либо переменной :
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
, каждый на отдельной строке, то эта единственная команда 3 sup> достаточно:
printf '%s\n' /tmp/dir{1..3}
2 : echo hello world
на самом деле работает: оно печатает hello world
, точно так же как echo 'hello world'
. Это потому, что echo
печатает один пробел между каждым повторяющимся аргументом . Однако echo hello world
не эквивалентен echo 'hello world'
: первый все еще печатает hello world
, а второй печатает hello world
. Sup>
3 : для более информацию о встроенном bash
printf
см. в выходных данных help printf
или этого раздела руководства Bash . printf
также является исполняемым файлом, который можно вызывать из других оболочек (и в этом качестве стандартизирован ). Sup>
Я согласен, что использование массива, как показано в других ответах, является лучшим способом решения реальной задачи ОП.
Но эти ответы не дают прямого ответа на поставленный вопрос, поэтому я сделаю это:
Вот как вы можете сделать это с помощью 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
Лучший способ - определить переменную 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- массив /