Używanie zmiennych wewnątrz bash heredoc

192

Próbuję interpolować zmienne w heredoc bash:

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

To nie działa, jak się spodziewałem ( $varjest traktowane dosłownie, a nie rozszerzone).

Muszę użyć, sudo teeponieważ utworzenie pliku wymaga sudo. Robiąc coś takiego:

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

Nie działa, ponieważ >outfileotwiera plik w bieżącej powłoce, która nie używa sudo.

Jon
źródło
9
To zrozumiałe zamieszanie! Jak zauważono poniżej, zacytowanie dowolnej części separatora wyłącza ekspansję w heredoc (tak jakby to było w środku ''), ale nie cytowanie separatora włącza ekspansję (tak jakby to było ""). Jednak intuicja jest poprawna w Perl, gdzie heredoc z pojedynczymi cytowany zachowuje się identyfikatorów jakby to było w apostrofach, jedna z podwójnym cytowany identyfikatora jakby w cudzysłowach, a jeden z tyłu zaznaczonych identyfikatora jakby backticks ! Patrz: perlop: << EOF
Nils von Barth

Odpowiedzi:

252

W odpowiedzi na twoje pierwsze pytanie nie ma podstawienia parametru, ponieważ umieściłeś separator w cudzysłowach - instrukcja bash mówi :

Format dokumentów tutaj to:

      <<[-]word
              here-document
      delimiter

Na słowie nie jest wykonywana interpretacja parametrów, podstawianie poleceń, interpretacja arytmetyczna ani interpretacja nazw ścieżek . Jeśli cytowane są jakiekolwiek znaki w słowie , separator jest wynikiem usunięcia cudzysłowu w słowie, a wiersze w niniejszym dokumencie nie są rozwijane. Jeżeli słowo nie jest cytowane, wszystkie wiersze dokumentu tutaj podlegają interpretacji parametrów, podstawianiu poleceń i interpretacji arytmetycznej. [...]

Jeśli zmienisz swój pierwszy przykład, aby użyć <<EOFzamiast << "EOF", przekonasz się, że to działa.

W drugim przykładzie powłoka wywołuje sudotylko parametr cat, a przekierowanie dotyczy danych wyjściowych sudo catjako pierwotny użytkownik. Będzie działać, jeśli spróbujesz:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT
Mark Longair
źródło
Jeśli jesteś zainteresowany, możesz to zrobić również jako: (cat > /path/to/outfile) <<EOFzamiastsudo sh -c ... <<EOF
Voltaire
Powiedz mi, że pochowany w Bash jest dobrym powodem, dla którego tak jest.
Landon Kuhn
96

Nie używaj cytatów z <<EOF:

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

Zmienna ekspansja jest domyślnym zachowaniem w tych dokumentach. Wyłączasz to zachowanie, cytując etykietę (z pojedynczymi lub podwójnymi cudzysłowami).

tłum
źródło
36

Jako późne następstwo wcześniejszych odpowiedzi, prawdopodobnie trafiasz w sytuacje, w których chcesz interpolować niektóre, ale nie wszystkie zmienne. Możesz to rozwiązać za pomocą ukośników odwrotnych, aby uniknąć znaków dolara i odwrotnych znaków; lub możesz umieścić tekst statyczny w zmiennej.

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

Demo: https://ideone.com/rMF2XA

Zauważ, że którykolwiek z mechanizmów cytowania - \____HERElub "____HERE"lub '____HERE'- wyłączy całą interpolację zmiennych i zamieni dokument tutaj w fragment dosłownego tekstu.

Częstym zadaniem jest łączenie zmiennych lokalnych ze skryptem, które powinny być oceniane przez inną powłokę, język programowania lub host zdalny.

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"
:
potrójny
źródło
3
Nie jestem pewien, dlaczego zostało to obniżone, ale dodaje ważną notatkę, która nie jest uwzględniona w wyżej głosowanej odpowiedzi.
Inian