Jak mogę napisać heredoc do pliku w skrypcie Bash?

Odpowiedzi:

1072

Przeczytaj Advanced Guide Bash-Scripting Guide Rozdział 19. Tutaj Dokumenty .

Oto przykład, który zapisze zawartość do pliku w /tmp/yourfilehere

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
        This line is indented.
EOF

Zauważ, że końcowe „EOF” (The LimitString) nie powinno zawierać żadnych białych znaków przed słowem, ponieważ oznacza to, że LimitStringnie zostaną one rozpoznane.

W skrypcie powłoki możesz użyć wcięcia, aby kod był czytelny, jednak może to mieć niepożądany efekt wcięcia tekstu w dokumencie tutaj. W takim przypadku użyj <<-(po którym następuje myślnik), aby wyłączyć wiodące tabulatory ( zwróć uwagę, że aby to przetestować, musisz zastąpić wiodące białe znaki tabulatorem , ponieważ nie mogę tutaj wydrukować rzeczywistych znaków tabulatora).

#!/usr/bin/env bash

if true ; then
    cat <<- EOF > /tmp/yourfilehere
    The leading tab is ignored.
    EOF
fi

Jeśli nie chcesz interpretować zmiennych w tekście, użyj pojedynczych cudzysłowów:

cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF

Aby potokować heredoc przez potok poleceń:

cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF

Wynik:

foo
bbr
bbz

... lub zapisać heredoc do pliku za pomocą sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF
Stefan Lasiewski
źródło
11
Nie potrzebujesz nawet Basha, ta funkcja jest również dostępna w powłokach Bourne / Korn / POSIX.
Janus Troelsen
5
a jak <<<oni się nazywają?
Jürgen Paul
18
@PineappleUndertheSea <<<nazywają się „Here Strings”. Kod podobny tr a-z A-Z <<< 'one two three'spowoduje łańcuch ONE TWO THREE. Więcej informacji na en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski,
8
Ostateczny EOF nie powinien również mieć po nim spacji . Przynajmniej podczas bashu powoduje to, że nie jest on rozpoznawany jako ogranicznik
carpii
10
Ponieważ ten konkretny heredok ma być dosłownie treścią, zamiast zawierać substytucje, powinien być <<'EOF'raczej <<EOF.
Charles Duffy
152

Zamiast używania catprzekierowania I / O przydatne może być użycie tee:

tee newfile <<EOF
line 1
line 2
line 3
EOF

Jest bardziej zwięzły, a w przeciwieństwie do operatora przekierowania można go łączyć, sudojeśli chcesz pisać do plików z uprawnieniami administratora.

Livven
źródło
18
Sugeruję dodanie > /dev/nullna końcu pierwszego wiersza, aby zapobiec wyświetlaniu zawartości pliku tutaj na standardowe wyjście podczas jego tworzenia.
Joe Carroll
11
To prawda, ale twoje rozwiązanie przypadło mi do gustu sudoraczej ze względu na jego kompatybilność niż z powodu zwięzłości :-)
Joe Carroll
2
Jak użyłbyś tej metody, aby dołączyć do istniejącego pliku?
MountainX
5
@MountainX Sprawdź man tee. Użyj -aflagi, aby dołączyć zamiast zastąpić.
Livven
3
Do użytku w skrypcie konfiguracyjnym, który czasem muszę nadzorować, podoba mi się ten kolejny, ponieważ drukuje zawartość.
Alois Mahdal
59

Uwaga:

Pytanie (jak napisać tutaj dokument (aka heredoc ) do pliku w skrypcie bash?) Ma (przynajmniej) 3 główne niezależne wymiary lub pytania:

  1. Czy chcesz zastąpić istniejący plik, dołączyć do istniejącego pliku lub napisać do nowego pliku?
  2. Czy Twój użytkownik lub inny użytkownik (np. root) Jest właścicielem pliku?
  3. Czy chcesz pisać zawartość swojego heredoc dosłownie, czy też chcesz, aby bash interpretował odniesienia do zmiennych w twoim heredoc?

(Istnieją inne wymiary / podzapytania, które nie uważam za ważne. Rozważ edycję tej odpowiedzi, aby je dodać!) Oto niektóre z ważniejszych kombinacji wymiarów pytania wymienionych powyżej, z różnymi różnymi ograniczającymi identyfikatorami - nie ma nic Najświętsze EOF, po prostu upewnij się, że ciąg, którego używasz jako identyfikatora, nie występuje w twoim heredoc:

  1. Aby zastąpić istniejący plik (lub napisać do nowego pliku), który posiadasz, zastępując odwołania do zmiennych wewnątrz heredoc:

    cat << EOF > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    EOF
  2. Aby dodać istniejący plik (lub zapisać do nowego pliku), który posiadasz, zastępując odniesienia do zmiennych wewnątrz heredoc:

    cat << FOE >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    FOE
  3. Aby zastąpić istniejący plik (lub zapisać nowy plik), którego jesteś właścicielem, dosłowną zawartością heredoc:

    cat << 'END_OF_FILE' > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    END_OF_FILE
  4. Aby dołączyć istniejący plik (lub zapisać do nowego pliku), który posiadasz, z dosłowną zawartością heredoc:

    cat << 'eof' >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    eof
  5. Aby zastąpić istniejący plik (lub zapisać do nowego pliku) będącego własnością root, zastępując odniesienia do zmiennych wewnątrz heredoc:

    cat << until_it_ends | sudo tee /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    until_it_ends
  6. Aby dołączyć istniejący plik (lub zapisać do nowego pliku) będącego własnością user = foo, z dosłowną zawartością heredoc:

    cat << 'Screw_you_Foo' | sudo -u foo tee -a /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    Screw_you_Foo
TomRoche
źródło
# 6 jest najlepszy. Ale jak zastąpić zawartość istniejącego pliku numerem 6?
Aleksandr Makov
2
@Aleksandr Makov: w jaki sposób nadpisujesz zawartość istniejącego pliku numerem 6? Pomiń -a== --append; tj. tee -a-> tee. Zobacz info tee(zacytuję to tutaj, ale znaczniki komentarzy są zbyt ograniczone
TomRoche,
1
Czy korzyść # 6 polega na użyciu kota i rurki do trójnika zamiast sudo tee /path/to/your/file << 'Screw_you_Foo'?
codemonkee
Dlaczego FOEzamiast EOFw dołączonym przykładzie?
becko,
2
@becko: tylko dla zilustrowania, że ​​etykieta jest tylko etykietą. Zauważ, że w każdym przykładzie użyłem innej etykiety.
TomRoche,
41

Aby skorzystać z odpowiedzi @ Livven , oto kilka przydatnych kombinacji.

  1. podstawianie zmiennych, zachowywanie wiodącej tabulacji, nadpisywanie pliku, echo na standardowe wyjście

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. brak podstawiania zmiennych , zachowywanie wiodącej tabulacji, nadpisywanie pliku, echo na standardowe wyjście

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. podstawianie zmiennych, usuwanie tabulacji wiodącej , nadpisywanie pliku, echo na standardowe wyjście

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. podstawianie zmiennych, zachowane tabulacje wiodące, dołączanie do pliku , echo na standardowe wyjście

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. podstawianie zmiennych, zachowywanie wiodącej tabulacji, nadpisywanie pliku, brak echa na standardowe wyjście

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. powyżej może być połączony z sudooraz

    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF
go2null
źródło
16

Gdy wymagane są uprawnienia roota

Jeśli do pliku docelowego wymagane są uprawnienia roota, użyj |sudo teezamiast >:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF
Serge Stroobandt
źródło
Czy możliwe jest przekazywanie zmiennych do dokumentów tutaj? Jak mogłeś to zdobyć, żeby $ FOO zostało zinterpretowane?
user1527227,
@ user1527227 Jak mówi przykładowy plik: zmienna $ FOO nie będzie interpretowana.
Serge Stroobandt
Poniżej próbowałem połączyć i uporządkować tę odpowiedź z odpowiedzią Stefana Lasiewskiego .
TomRoche
3
@ user1527227 Po prostu nie dołączaj EOF do pojedynczych cudzysłowów. Wtedy $ FOO zostanie zinterpretowany.
imnd_neel
11

Dla przyszłych osób, które mogą mieć ten problem, działał następujący format:

(cat <<- _EOF_
        LogFile /var/log/clamd.log
        LogTime yes
        DatabaseDirectory /var/lib/clamav
        LocalSocket /tmp/clamd.socket
        TCPAddr 127.0.0.1
        SelfCheck 1020
        ScanPDF yes
        _EOF_
) > /etc/clamd.conf
Joshua Enfield
źródło
6
Nie potrzebuj nawiasów: cat << END > afilenastępnie heredoc działa doskonale.
glenn jackman
Dzięki, to faktycznie rozwiązało kolejny problem, na który wpadłem. Po kilku tutaj dokumentach pojawiły się problemy. Myślę, że miało to związek z parenami, ponieważ powyższe porady to naprawiły.
Joshua Enfield,
2
To nie zadziała. Przekierowanie wyjścia musi znajdować się na końcu wiersza, który zaczyna się od, catjak pokazano w zaakceptowanej odpowiedzi.
Wstrzymano do odwołania.
2
@DennisWilliamson Działa, po to są pareny. Całość catdziała w podpowłoce, a wszystkie dane wyjściowe podpowłoki są przekierowywane do pliku
Izkata
2
@Izkata: Jeśli spojrzysz na historię edycji tej odpowiedzi, nawiasy zostały usunięte, zanim skomentowałem i dodałem je później. dotyczy komentarza glenna jackmana (i mojego).
Wstrzymano do odwołania.
3

Jako przykład możesz go użyć:

Po pierwsze (nawiązanie połączenia ssh):

while read pass port user ip files directs; do
    sshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directs
done <<____HERE
    PASS    PORT    USER    IP    FILES    DIRECTS
      .      .       .       .      .         .
      .      .       .       .      .         .
      .      .       .       .      .         .
    PASS    PORT    USER    IP    FILES    DIRECTS
____HERE

Po drugie (wykonywanie poleceń):

while read pass port user ip; do
    sshpass -p$pass ssh -p $port $user@$ip <<ENDSSH1
    COMMAND 1
    .
    .
    .
    COMMAND n
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP
      .      .       .       .
      .      .       .       .
      .      .       .       .
    PASS    PORT    USER    IP    
____HERE

Po trzecie (wykonywanie poleceń):

Script=$'
#Your commands
'

while read pass port user ip; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip "$Script"

done <<___HERE
PASS    PORT    USER    IP
  .      .       .       .
  .      .       .       .
  .      .       .       .
PASS    PORT    USER    IP  
___HERE

Dalej (przy użyciu zmiennych):

while read pass port user ip fileoutput; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip fileinput=$fileinput 'bash -s'<<ENDSSH1
    #Your command > $fileinput
    #Your command > $fileinput
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP      FILE-OUTPUT
      .      .       .       .          .
      .      .       .       .          .
      .      .       .       .          .
    PASS    PORT    USER    IP      FILE-OUTPUT
____HERE
MLSC
źródło
-1

Podoba mi się ta metoda zwięzłości, czytelności i prezentacji w wciętym skrypcie:

<<-End_of_file >file
       foo bar
End_of_file

Gdzie →       jest tabpostać.

dan
źródło