Як я можу написати гередок у файл у сценарії Bash?


Відповіді:


1072

Прочитайте Розширений посібник із написання сценаріїв Розділ 19. Тут Документи .

Ось приклад, який запише вміст у файл на /tmp/yourfilehere

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

Зауважте, що підсумковий "EOF" (The LimitString) не повинен мати жодного пробілу перед словом, оскільки це означає, що LimitStringзаповіт не буде розпізнаний.

У скрипті оболонки ви можете використовувати відступ, щоб зробити код читабельним, однак це може мати небажаний ефект відступу тексту у вашому тут документі. У цьому випадку використовуйте <<-(з подальшим тире) для відключення провідних вкладок ( Зверніть увагу, що для перевірки цього вам потрібно буде замінити провідний пробіл символом вкладки , оскільки я не можу надрукувати тут фактичні символи вкладки.)

#!/usr/bin/env bash

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

Якщо ви не хочете інтерпретувати змінні в тексті, використовуйте одиничні лапки:

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

Щоб передати гередок через командний конвеєр:

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

Вихід:

foo
bbr
bbz

... або записати гередок у файл, використовуючи sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF

11
Вам навіть не потрібен Bash, ця функція є і в оболонках Bourne / Korn / POSIX.
Янус Троельсен

5
про <<<що, як вони називаються?
Юрген Пол

18
@PineappleUndertheSea <<<називаються "Тут рядки". Код подібний tr a-z A-Z <<< 'one two three'призведе до рядка ONE TWO THREE. Більше інформації на en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski

8
Кінцевий EOF також не повинен мати жодного пробілу після нього. Принаймні на баші, це призводить до того, що він не
визнається

10
Оскільки саме цей гередок призначений для прямого змісту, а не для заміни, він повинен бути, <<'EOF'а не <<EOF.
Чарльз Даффі

152

Замість використання catта переадресації вводу / виводу може бути корисним teeзамість цього:

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

Це більш стисло, плюс на відміну від оператора переадресації, його можна комбінувати, sudoякщо вам потрібно записувати у файли з кореневими правами.


18
Я б запропонував додати > /dev/nullв кінці першого рядка, щоб запобігти відображенню вмісту файлу тут, щоб stdout, коли він створений.
Джо Керролл

11
Щоправда, але ваше рішення сподобалось мені через його сумісність sudo, а не через його стислість :-)
Джо Керролл

2
Як би ви використовували цей метод для додавання до вже наявного файлу?
MountainX

5
@MountainX Виїзд man tee. Використовуйте -aпрапор для додавання замість перезапису.
Livven

3
Для використання в конфігураційному сценарії, який мені іноді потрібно переглядати, мені це більше подобається, оскільки він друкує вміст.
Алоїс Магдал

59

Примітка:

Питання (як написати тут документ (він же heredoc ) у файл у баш-скрипті?) Містить (принаймні) 3 основні незалежні розміри або підзаписи:

  1. Ви хочете перезаписати існуючий файл, додати до вже наявного файлу чи написати новий файл?
  2. Ваш rootвласник або інший користувач (наприклад, ) володіє файлом?
  3. Ви хочете записати вміст свого гередока буквально, або щоб баш інтерпретував змінні посилання всередині вашого гередока?

(Є й інші параметри / підпункти, які я не вважаю важливими. Розгляньте можливість редагування цієї відповіді, щоб додати їх!) Ось кілька важливіших комбінацій розмірів цього питання, перелічених вище, з різними ідентифікаторами, що розмежовують - нічого немає священо EOF, просто переконайтеся, що рядок, який ви використовуєте як свій обмежувальний ідентифікатор, не зустрічається всередині вашого heredoc:

  1. Щоб перезаписати існуючий файл (або записати у новий файл), який ви є власником, замінивши посилання змінних всередині 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. Щоб додати наявний файл (або записати в новий файл), який ви є власником, замінюючи посилання змінних всередині 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. Щоб перезаписати наявний файл (або записати в новий файл), який ви є власником, з буквальним вмістом гередока:

    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. Щоб додати наявний файл (або записати в новий файл), який є у вас, з буквальним вмістом гередока:

    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. Щоб перезаписати існуючий файл (або записати в новий файл), що належить root, замінивши посилання змінних всередині 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. Щоб додати існуючий файл (або записати в новий файл), що належить користувачеві = foo, з буквальним вмістом гередока:

    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

№6 найкраще. Але як перезаписати вміст існуючого файлу за допомогою №6?
Олександр Маков

2
@ Олександр Маков: як ви перезаписуєте вміст існуючого файлу на номер 6? Пропустіть -a== --append; тобто, tee -a-> tee. Дивіться info tee(я б це процитував тут, але розмітка коментарів занадто обмежена.
TomRoche

1
Чи є користь для №6 використання кота та трубопроводу до трійника замість sudo tee /path/to/your/file << 'Screw_you_Foo'?
codemonkee

Чому FOEзамість EOFприкладу, що додається?
бекко

2
@becko: просто щоб проілюструвати, що лейбл - це лише мітка. Зауважте, що в кожному прикладі я використовував іншу мітку.
TomRoche

41

Для того, щоб побудувати на @ Livven в відповідь , ось деякі корисні комбінації.

  1. заміна змінної, збережена ведуча вкладка, перезапис файлу, відлуння до stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. відсутність заміни змінної , збережена ведуча вкладка, перезапис файлу, відлуння до stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. заміна змінної, ведуча вкладка видалена , перезапис файлу, відлуння до stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. заміна змінної, ведуча вкладка збережена, додавання до файлу , відлуння до stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. заміна змінної, збережена ведуча вкладка, перезаписування файлу, відсутність відлуння до stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. вище , можуть бути об'єднані з , sudoа також

    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF

16

Коли потрібні дозволи root

Коли для |sudo teeкореспондентського файлу потрібні права доступу, використовуйте замість >:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF

Чи можна передати змінні сюди документи? Як ви могли це отримати, щоб інтерпретували $ FOO?
користувач1527227

@ user1527227 Як показано в прикладі цього файлу: Змінна $ FOO не буде інтерпретована.
Серж Стротобандт

Нижче я намагався поєднати та організувати цю відповідь із відповіддю Стефана Ласєвського .
TomRoche

3
@ user1527227 Просто не вкладайте EOF в окремі лапки. Тоді $ FOO буде інтерпретовано.
imnd_neel

11

Для майбутніх людей, які можуть мати цю проблему, працював наступний формат:

(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

6
Не потрібні дужки: cat << END > afileслідом за ним гередок працює чудово.
glenn jackman

Дякую, це фактично вирішило ще одне питання, в яке я зіткнувся. Після кількох тут документів виникли деякі проблеми. Я думаю, що це було пов'язано з паренами, як і з порадами вище, які вона виправила.
Джошуа Енфілд

2
Це не спрацює. Перенаправлення виводу має бути в кінці рядка, який починається з, catяк показано у прийнятій відповіді.
Призупинено до подальшого повідомлення.

2
@DennisWilliamson Це працює, ось для чого потрібні парени. Весь catпробіг знаходиться в підзаголовці, а весь вихід
підпакетів

2
@Izkata: Якщо ви подивитесь на історію редагування цієї відповіді, круглі дужки були видалені до того, як я зробив свій коментар, і потім додав їх назад. (і мій) коментар Глена Джекмана стосується.
Призупинено до подальшого повідомлення.

3

Як екземпляр ви можете використовувати його:

Спочатку (встановлення 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

Друге (виконання команд):

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

Третє (виконання команд):

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

Далі (з використанням змінних):

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

-1

Мені подобається цей метод для стислість, читабельності та подання у відрізному сценарії:

<<-End_of_file >file
       foo bar
End_of_file

Де →       це tabсимвол.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.