Получение значений из переменных - скрипт bash

Я пытаюсь написать 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
4
задан 06.05.2020, 22:23

4 ответа

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
3
ответ дан 06.05.2020, 22:24

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? в Переполнение стека .

Некоторые предупреждения (которые также относятся к некоторым другим опубликованным ответам):

  • Если какие-либо значения INPUTn могут содержит пробелы 1 (подобно пробелам 2 , табуляции или [ 1173] должны использоваться конструкции новой строки ) или со специальными символами, которые будут расширены (например, $varname), с цитированием .

    Одиночные кавычки для назначения лучше всего подходят, если вы хотите предотвратить все раскрытие в них:
    INPUT1=/tmp/dir1INPUT1='/tmp/dir with spaces'

    (Только символ кавычки ' обрабатываются специально, так как это будет действовать как закрывающая кавычка.)

    Но используйте двойные кавычки, чтобы разрешить расширение переменной:
    echo ${!s}echo "${!s}"

    (Это работает, даже если значение после расширения содержит $. Расширенное содержимое больше не раскрывается.
    Например, BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO" печатает BAR $BAZ, [ 1188] not BAR QUUX.)

  • Если любой из INPUTn может принимать значение, которое echo будет интерпретироваться как опция вместо текста для печати замените echo на printf '%s\n'. 3

    В настоящее время это -, за которым следует еще один 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 достаточно:

printf '%s\n' /tmp/dir{1..3}

1 : оболочка разбивает текст на слова [ 1177] на пустом месте или, если переменная 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 также является исполняемым файлом, который можно вызывать из других оболочек (и в этом качестве стандартизирован ).

0
ответ дан 06.05.2020, 22:24

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

Вот как вы можете сделать это с помощью 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
3
ответ дан 06.05.2020, 22:25

Лучший способ - определить переменную 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- массив /

0
ответ дан 06.05.2020, 22:25
  • 1
    @Ubuntuser - Вы оболочка удара использования, когда Вы отметили свой вопрос? Или как Вы выполняете сценарий? – Sagar 06.05.2020, 22:25
  • 2
    Да его удар в Ubuntu 13.04. for i in {0,1,2}; работы для меня – KernelPanic 06.05.2020, 22:25
  • 3
    Это не работает. " поскольку я в " $ {ВХОД} " ". я получаю следующую ошибку: синтаксическая ошибка: операнд ожидал (ошибочный маркер.. – RichardTheKiwi 06.05.2020, 22:26
  • 4
    ./script-name.sh то, что я использую. – osrpt 06.05.2020, 22:26
  • 5
    @Ubuntuser, Как Вы выполняете сценарий? – bjesua 06.05.2020, 22:27