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?


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 `.


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


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"

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.