Jak pobrać << EOF >> plik zawierający kod?

104

Chcę wydrukować kod do pliku za pomocą cat <<EOF >>:

cat <<EOF >> brightup.sh
!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

ale kiedy sprawdzam dane wyjściowe pliku, otrzymuję to:

!/bin/bash
curr=1634
if [  -lt 4477 ]; then
   curr=406;
   echo   > /sys/class/backlight/intel_backlight/brightness;
fi

Próbowałem umieszczać pojedyncze cudzysłowy, ale wynik zawiera również pojedyncze cudzysłowy. Jak mogę uniknąć tego problemu?

UberNate
źródło
2
Powinieneś także naprawić shebang. Pierwsza linijka musi być dosłownie #!/bin/bashi nic więcej - #!to właśnie sprawia, że ​​jest to poprawna linia shebang, a to, co następuje po niej, to ścieżka do tłumacza.
tripleee
1
zobacz man bashi wyszukaj Here Documents. Wszystkie szczegóły.
Hong,
1
Jako późno wychodzący na bok, nowoczesna składnia podstawiania procesów jest $(command)zamiast `command`. Aby uzyskać zawartość pliku, Bash ma$(<file)
tripleee

Odpowiedzi:

165

Potrzebujesz tylko minimalnej zmiany; Cudzysłów separator dokumentu znajdującego się tutaj po <<.

cat <<'EOF' >> brightup.sh

lub równoważnie z ukośnikiem odwrotnym:

cat <<\EOF >>brightup.sh

Bez cytowania, dokument w tym miejscu zostanie zastąpiony zmiennymi, znaki odwrotne zostaną ocenione itd., Tak jak odkryłeś.

Jeśli potrzebujesz poszerzyć niektóre wartości, ale nie wszystkie, musisz indywidualnie uciec od tych, którym chcesz zapobiec.

cat <<EOF >>brightup.sh
#!/bin/sh
# Created on $(date # : <<-- this will be evaluated before cat;)
echo "\$HOME will not be evaluated because it is backslash-escaped"
EOF

będzie produkować

#!/bin/sh
# Created on Fri Feb 16 11:00:18 UTC 2018
echo "$HOME will not be evaluated because it is backslash-escaped"

Zgodnie z sugestią @fedorqui , oto odpowiednia sekcja z man bash:

Tutaj Dokumenty

Ten typ przekierowania instruuje powłokę, aby odczytywała dane wejściowe z bieżącego źródła, aż do wyświetlenia wiersza zawierającego tylko separator (bez końcowych spacji). Wszystkie wiersze odczytane do tego momentu są następnie używane jako standardowe wejście polecenia.

Format dokumentów tutaj:

      <<[-]word
              here-document
      delimiter

Na słowie nie jest wykonywana interpretacja parametrów, podstawianie poleceń, interpretacja wyrażeń arytmetycznych ani rozwijanie nazw plików. Jeśli jakiekolwiek znaki w słowie są cytowane, separator jest wynikiem usunięcia cudzysłowu w słowie, a wiersze w dokumencie włączonym nie są rozwijane. Jeśli słowo nie jest cytowane, wszystkie wiersze dokumentu włączonego podlegają interpretacji parametrów, podstawianiu poleceń i interpretacji wyrażeń arytmetycznych . W tym drugim przypadku sekwencja znaków \ ​​jest ignorowana, a \ należy użyć do cytowania znaków \, $ i `.

tripleee
źródło
1
Widzę, że zaznaczyłeś Jak uniknąć rozwijania zmiennych heredoc? jako duplikat tego. Żadnych zastrzeżeń, tylko że dołączyłbym referencje Bash docs, tak jak to zrobiłem w moim (które mając tylko 13 tys. Wizyt otrzymało prawie 100 powtórzeń, więc wydaje się być całkiem pomocne).
fedorqui 'SO przestań szkodzić'
@fedorqui Może chcesz właściwie przenieść swoją odpowiedź na to pytanie? Albo możemy zamienić zduplikowane oznaczenie, aby było odwrotnie; Spojrzałem tylko na wynik pytania, a nie na odpowiedź.
tripleee
Mmm co z połączeniem ich, mając to pytanie jako docelowe? Duplikat pytania ma dobry tytuł, tyle że jest za długi. Jednak ostatnio w jakiś sposób przyciągnęło to sporo uwagi (co miesiąc dostaję kilka pozytywnych głosów ).
fedorqui 'SO przestań szkodzić'
@fedorqui Podoba mi się koncepcja łączenia, ale nigdy nie widziałem jej w praktyce; jeśli dobrze rozumiem sytuację, mody po prostu nie są w stanie poradzić sobie z nietrywialnymi połączeniami, ponieważ kłopoty i złożoność znacznie przewyższają korzyści. To, co zrobiłem raz lub dwa, to usunięcie przydatnej i pozytywnie ocenionej odpowiedzi i opublikowanie jej ponownie pod innym pytaniem, chociaż nie mogę polecić tego obejścia.
tripleee
Trudno powiedzieć, kiedy warto to zrobić. Zrobiłem to na stronie, którą modowałem, kiedy pojawiło się dobre pytanie, nie zauważając, że było inne dobre pytanie wcześniej, obie z dobrymi odpowiedziami. Usunięcie odpowiedzi na wynik 90+ nie wydaje się dobrym planem, ponieważ pozostawiłoby to pytanie osierocone.
fedorqui 'SO przestań szkodzić'
20

Lub, używając swoich znaczników EOF, musisz zacytować początkowy znacznik, aby ekspansja nie została wykonana:

#-----v---v------
cat <<'EOF' >> brightup.sh
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

IHTH

łuskarz
źródło
1
Zmieniłbym Twój post, ale dla pewności nie powinien to być #! / Bin / bash, a nie! / Bin / bash?
Matthew Hoggan
@MatthewHoggan: Tak, masz rację! Dzięki, że to złapałeś. Naprawiam to teraz.
łowca
16

To powinno działać, właśnie przetestowałem to i działało zgodnie z oczekiwaniami: nie miało miejsca żadne rozszerzenie, zamiana ani to, co masz.

cat <<< '
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
  curr=$((curr+406));
  echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi' > file # use overwrite mode so that you don't keep on appending the same script to that file over and over again, unless that's what you want. 

Korzystanie z poniższych również działa.

cat <<< ' > file
 ... code ...'

Warto również zauważyć, że podczas korzystania z heredoców ma miejsce, takie jak << EOFpodstawianie i rozwijanie zmiennych i tym podobne. Robiąc coś takiego:

cat << EOF > file
cd "$HOME"
echo "$PWD" # echo the current path
EOF

zawsze spowoduje rozszerzenie zmiennych $HOMEi $PWD. Więc jeśli twój katalog domowy to, /home/foobara obecna ścieżka to /home/foobar/bin, filebędzie wyglądać następująco:

cd "/home/foobar"
echo "/home/foobar/bin"

zamiast oczekiwanego:

cd "$HOME"
echo "$PWD"
Alexej Magura
źródło
2
Ciąg znaków tutaj w pojedynczych cudzysłowach oczywiście nie może zawierać żadnych pojedynczych cudzysłowów, co może stanowić przeszkodę. Tutaj dokumenty są jedynym rozsądnym obejściem, jeśli masz skrypt, który musi zawierać zarówno pojedyncze, jak i podwójne cudzysłowy, chociaż nie jest to oczywiście w przypadku prostego przykładu OP. Ponadto, stycznie, ciągi tutaj <<<są dostępne tylko począwszy od Bash 3 i nie można ich przenosić na inne powłoki.
tripleee
<<<jest również dostępny w Zsh
Alexej Magura
3
Dzisiaj dowiedziałem się, że możesz mieć nazwę pliku bezpośrednio po otwarciu do heredoc. Dzięki @AlexejMagura!
chaseadamsio
0

Wiem, że to pytanie sprzed dwóch lat, ale jest to szybka odpowiedź dla tych, którzy szukają „jak to zrobić”.

Jeśli nie chcesz umieszczać niczego w cudzysłowie, możesz po prostu napisać blok tekstu do pliku i zmienić zmienne, które chcesz wyeksportować jako tekst (na przykład do użycia w skrypcie) i nie uciec przed tymi, które chcesz eksportuj jako wartość zmiennej.

#!/bin/bash

FILE_NAME="test.txt"
VAR_EXAMPLE="\"string\""

cat > ${FILE_NAME} << EOF
\${VAR_EXAMPLE}=${VAR_EXAMPLE} in ${FILE_NAME}  
EOF

Zapisuje "$ {VAR_EXAMPLE} =" string "w test.txt" do test.txt

Można to również użyć do wyprowadzenia bloków tekstu do konsoli z tymi samymi regułami, pomijając nazwę pliku

#!/bin/bash

VAR_EXAMPLE="\"string\""

cat << EOF
\${VAR_EXAMPLE}=${VAR_EXAMPLE} to console 
EOF

Wyświetla "$ {VAR_EXAMPLE} =" ciąg "do konsoli" do konsoli

GCUGreyArea
źródło