Использование переменных внутри bash heredoc

192

Я пытаюсь интерполировать переменные внутри bash heredoc:

var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF

Это не работает, как я ожидал ( $varтрактуется буквально, а не расширенно).

Мне нужно использовать, sudo teeпотому что создание файла требует sudo. Делать что-то вроде:

sudo cat > /path/to/outfile <<EOT
my text...
EOT

Не работает, потому что >outfileоткрывает файл в текущей оболочке, которая не использует sudo.

Джон
источник
9
Это понятная путаница! Как отмечено ниже, цитирование любой части разделителя отключает расширение в heredoc (как если бы оно было ''), но не цитирование разделителя включает расширение (как если бы оно было ""). Тем не менее, ваша интуиция верна в Perl, где heredoc с идентификатором в одинарных кавычках ведет себя так, как если бы он был в одинарных кавычках, один с идентификатором в двойных кавычках, как будто в двойных кавычках, и один с идентификатором в обратном тиканье, как будто в обратных галочках ! Смотрите: perlop: << EOF
Нильс фон Барт

Ответы:

252

В ответ на ваш первый вопрос нет подстановки параметров, потому что вы поместили разделитель в кавычки - руководство по bash говорит :

Формат здесь-документов:

      <<[-]word
              here-document
      delimiter

Для слова не выполняется расширение параметров, подстановка команд, арифметическое расширение или расширение имени пути . Если какие-либо символы в слове заключены в кавычки, разделитель является результатом удаления кавычки в слове, а строки в документе здесь не раскрываются. Если слово не заключено в кавычки, все строки здесь-документа подвергаются расширению параметров, подстановке команд и арифметическому расширению. [...]

Если вы измените свой первый пример на использование <<EOFвместо, то << "EOF"вы обнаружите, что он работает.

Во втором примере оболочка вызывается sudoтолько с параметром cat, а перенаправление применяется к выводу от sudo catимени исходного пользователя. Это сработает, если вы попробуете:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT
Марк Лонгэйр
источник
Если вам интересно, вы также можете сделать это следующим образом: (cat > /path/to/outfile) <<EOFвместоsudo sh -c ... <<EOF
Вольтер
Пожалуйста, скажите мне, что похоронить в Баше - хорошая причина, почему это так.
Лэндон Кун
96

Не используйте кавычки с <<EOF:

var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF

Расширение переменной является поведением по умолчанию внутри here-документов. Вы отключаете это поведение, заключая в кавычки (одинарные или двойные кавычки).

чернь
источник
36

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

Name='Rich Ba$tard'
dough='$$$dollars$$$'
cat <<____HERE
$Name, you can win a lot of $dough this week!
Notice that \`backticks' need escaping if you want
literal text, not `pwd`, just like in variables like
\$HOME (current value: $HOME)
____HERE

Демо: https://ideone.com/rMF2XA

Обратите внимание, что любой из механизмов цитирования - \____HEREили "____HERE"или '____HERE'- отключит интерполяцию всех переменных и превратит документ здесь в фрагмент буквального текста.

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

local=$(uname)
ssh -t remote <<:
    echo "$local is the value from the host which ran the ssh command"
    # Prevent here doc from expanding locally; remote won't see backslash
    remote=\$(uname)
    # Same here
    echo "\$remote is the value from the host we ssh:ed to"
:
tripleee
источник
3
Не уверен, почему за него проголосовали, но он добавляет действительное примечание, которое не отражено в ответе с более высоким рейтингом.
Инь