Разница между ${} и $() в скрипте shell

Вопрос или проблема

$ echo $(date)
Thu Jul 2 16:33:11 SGT 2015
$ echo ${date}

$ name=foo
$ echo $(name)
ksh: name:  not found

$ echo ${name}
foo

Кажется, что ${variable} — это то же самое, что и $variable, в то время как $() используется для выполнения команды. Зачем тогда использовать ${}?

$(command) — это «замена команды». 
Как вы, вероятно, понимаете, он выполняет command, захватывает его вывод,
и вставляет его в строку команды, содержащую $(…); например,

$ ls -ld $(date +%B).txt
-rwxr-xr-x  1 Noob Noob    867 Jul  2 11:09 July.txt

${parameter} — это «замена параметра». 
Много информации можно найти в мануале shell, bash(1),
в разделе «Расширение параметров»:

${parameter}

    Значение parameter подставляется. 
    Фигурные скобки требуются, когда parameter является позиционным параметром
    с более чем одной цифрой, или когда parameter следует за символом,
    который не должен интерпретироваться как часть его имени.

Для позиционных параметров смотрите «Positional Parameters» ниже. 
В наиболее распространенном использовании, как показано в других ответах,
parameter — это имя переменной. 
Форма ${…}, как указано в конце приведенного выше абзаца,
позволяет получить значение переменной
(т.е. $variable_name)
и сразу следовать за буквой, цифрой или подчеркиванием:

$ animal=cat
$ echo $animals
                                # Нет такой переменной, как “animals”.
$ echo ${animal}s
cats
$ echo $animal_food
                                # Нет такой переменной, как “animal_food”.
$ echo ${animal}_food
cat_food

Вы также можете сделать это с кавычками:

$ echo "$animal"s
cats

Или, как упражнение в вариантах, вы можете использовать вторую переменную:

$ plural=s
$ echo $animal$plural
cats

Но это только шаг 1. 
Следующий параграф в мануале интересный, хотя и немного запутанный:

Если первый символ parameter
является восклицательным знаком (!),
вводится уровень косвенной переменной. 
Bash использует значение переменной, образованное из оставшейся части parameter
в качестве имени переменной; затем эта переменная разворачивается
и это значение используется в остальной части замены,
вместо значения самого parameter
Это называется косвенной заменой.    
(исключения)  
Восклицательный знак должен непосредственно следовать за левой скобкой
чтобы ввести косвенность.

Я не уверен, как можно это сделать понятнее, кроме как на примере:

$ animal=cat
$ echo $animal
cat
$ cat=tabby
$ echo $cat
tabby
$ echo ${!animal}
tabby                           # Если $animal это “cat”, тогда ${!animal}  это $cat, т.е. “tabby”

Так что давайте назовем это шагом 1½. 
Есть много интересных вещей, которые можно сделать как шаг 2:

$ animal=cat
$ echo ${#animal}
3                               # Длина строки
$ echo ${animal/at/ow}
cow                             # Замена

Вы не можете сделать ни одну из этих вещей без фигурных скобок {}.

Positional Parameters

Рассмотрим этот искусственный пример:

$ cat myecho.sh
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15
$ ./myecho.sh Hey diddle diddle, The cat and the fiddle, The cow jumped over the moon.
Hey diddle diddle, The cat and the fiddle, The Hey0 Hey1 Hey2 Hey3 Hey4 Hey5

поскольку shell не понимает $10, $11 и т.д. 
Он трактует $10 как ${1}0
Но он понимает ${10}, ${11} и т.д., как упомянуто в мануале
(«позиционный параметр с более чем одной цифрой»).

Но не следует писать скрипты таким образом;
существуют лучшие способы работы с длинными списками аргументов.

Приведенное выше (вместе с многими другими формами
конструкций ${parameter…something_else})
обсуждается более подробно в мануале shell, bash(1).

А Да вспомним о кавычках

Обратите внимание, что вам всегда следует приводить переменные shell в кавычках,
если у вас нет веской причины не делать этого,
и вы уверены, что знаете, что делаете. 
Напротив, хотя фигурные скобки иногда важны,
они не так важны, как кавычки.

$ filename="nursery rhyme.txt"
$ ls -ld ${filename}
ls: cannot access nursery: No such file or directory
ls: cannot access rhyme.txt: No such file or directory
$ ls -ld "$filename"
-rwxr-xr-x  1 Noob Noob   5309 Jul  2 11:09 nursery rhyme.txt

Это также относится к позиционным параметрам
(т.е. аргументам командной строки; например, "$1") и также к замене команды:

$ ls -ld $(date "+%B %Y").txt
ls: cannot access July: No such file or directory
ls: cannot access 2015.txt: No such file or directory
$ ls -ld "$(date "+%B %Y").txt"
-rwxr-xr-x  1 Noob Noob    687 Jul  2 11:09 July 2015.txt

Смотрите Bash quotes unescaped on command substitution
для краткого изложения взаимодействия между кавычками и $().

В вашем примере $var и ${var} идентичны. Однако фигурные скобки полезны, когда вы хотите расширить переменную в строке:

    $ string=foo
    $ echo ${string}bar
      foobar
    $ echo $stringbar

    $ 

Таким образом, фигурные скобки предоставляют средство для подстановки переменной, чтобы получить имя новой переменной, которая также будет подставлена.

Я обычно вижу это чаще в строках. Такое не сработает:

var="a"
echo "$varRAW_STRING"

Но это сработает:

var="a"
echo "${var}RAW_STRING"

Как вы правильно сказали, $() используется для выполнения команды:

dir_contents=$(ls)

Можно также использовать обратные кавычки, но я считаю $() более универсальным. Например, обратные кавычки нельзя (легко) вложить.

date_directory=`ls `date '+%Y-%m-%d'`` # Не имеет смысла
date_directory=$(ls $(date '+%Y-%m-%d')) # Гораздо лучше

Ответ или решение

Вопрос о различиях между ${} и $() в сценариях оболочки является важным и требует четкого понимания концепций замены параметров и команд. Разберем это более детально.

Теоретическая часть

  1. Замена параметров: ${}

    Замена параметров — это механизм оболочки, позволяющий работать с переменными. Используя ${}, можно получить значение переменной и при необходимости провести дополнительные манипуляции с ним. К примеру, конструкция ${variable} обычно эквивалентна просто $variable, но использование фигурных скобок необходимо в некоторых случаях. Например, когда имя переменной идет рядом с другим текстом или символами, чтобы избежать неоднозначности или ошибок в синтаксисе.

    Основные ситуации, в которых фигурные скобки обязательны:

    • Когда потребуется указать переменную с числовым индексом, имеющим более одной цифры, например, ${10}.
    • При необходимости использования дополнительных возможностей замены параметров, таких как извлечение длины строки или замена части строки.
  2. Замена команд: $()

    Замена команд используется для выполнения команды в процессе выполнения сценария и подстановки вывода этой команды вместо самой конструкции. Это полезно для динамической генерации данных. Например, можно использовать для получения текущей даты или времени, как в примере $(date).

Примеры

  • Пример с заменой параметров:

    animal="cat"
    echo $animals      # Выведет пустую строку, переменной animals нет.
    echo ${animal}s    # Вернется "cats", потому что фигурные скобки позволяют обойти переменную и добавить "s".
  • Пример с заменой команд:

    current_date=$(date "+%Y-%m-%d")
    echo "Today's date is: $current_date"  # Будет выведена текущая дата в заданном формате.

Применение

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

  • Доступ к файловой системе на основе текущей даты:

    backup_dir="/backup/$(date +%Y-%m-%d)"
    mkdir -p "$backup_dir"
    echo "Backup directory created: $backup_dir"

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

  • Создание переменных на основе пользовательского ввода:

    user_input="data"
    file_name="${user_input}_file.txt"
    echo "File name: $file_name"

    Программа целенаправленно использует ${} для объединения переменной с текстом, чтобы избежать ошибок в разрешении имен переменных.

Заключение

Понимание различий между ${} и $() в оболочке Bash позволяет оптимально использовать ресурсы ОС и делает ваши скрипты более корректными и производительными. Концепции замены параметров и команд имеют много практических применений, от обработки строк до автоматизации системных задач. Применяя эти концепции в сценариях, можно добиться более эффективного и гибкого управления данными и операциями в оболочке Bash.

Надеюсь, это разъяснение поможет вам точно интерпретировать и применять эти конструкции в ваших проектах.

Оцените материал
Добавить комментарий

Капча загружается...