Як котувати << EOF >> файл, що містить код?


101

Я хочу надрукувати код у файл, використовуючи 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

але коли я перевіряю вихідні дані файлу, я отримую таке:

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

Я спробував поставити одинарні лапки, але результат також містить окремі лапки. Як я можу уникнути цієї проблеми?


2
Слід також виправити шебанг. Перший рядок повинен бути буквально #!/bin/bashі нічим іншим - #!це те, що робить його дійсним рядком шебанга, а те, що настає після нього, - це шлях до перекладача.
триплі

1
бачити man bashі шукати Here Documents. Всі деталі є.
Гонг

1
Як пізніше, сучасний синтаксис заміщення процесу $(command)замість `command`. Для отримання вмісту файлу Баш$(<file)
тричі

Відповіді:


159

Вам потрібні лише мінімальні зміни; одинарними лапками розділювач тут документа після <<.

cat <<'EOF' >> brightup.sh

або еквівалентно зворотну косу риску:

cat <<\EOF >>brightup.sh

Без цитування цей документ зазнає заміни змінною, зворотні позначки будуть оцінені тощо, як ви виявили.

Якщо вам потрібно розширити деякі, але не всі значення, вам потрібно індивідуально уникати тих, які ви хочете запобігти.

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

буде виробляти

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

Як запропонував @fedorqui , ось відповідний розділ від man bash:

Тут Документи

Цей тип переспрямування вказує оболонці зчитувати вхідні дані з поточного джерела, поки не буде видно рядок, що містить лише роздільник (без порожніх пробілів). Потім усі рядки, прочитані до цієї точки, використовуються як стандартний вхід для команди.

Формат тут-документів:

      <<[-]word
              here-document
      delimiter

Розширення параметрів, підстановка команд, арифметичне розширення або розширення імені шляху не виконується для слова. Якщо в слові вказані будь-які символи, роздільник є результатом видалення лапки з слова, а рядки в документі тут не розширюються. Якщо слово без лапок, усі рядки документа тут підлягають розширенню параметрів, підстановці команд та арифметичному розширенню . В останньому випадку послідовність символів \ ігнорується, і для цитування символів \, $ та `потрібно використовувати \.


1
Я бачу, що ви позначені Як уникнути змінних heredoc, що розширюються? як дублікат цього. Жодних заперечень, лише те, що я включив би посилання на документи Bash, як це робив у своєму (який, маючи лише 13 тис. Відвідувань, отримав майже 100 повторень, тому, здається, це було б дуже корисно).
Fedorqui 'SO prestani шкодити'

@fedorqui Можливо, ви хочете перенести свою відповідь на це питання насправді? Або ми можемо змінити дублікат маркування на інше; Я просто подивився на оцінку запитань, а не на бали відповідей.
триплі

Ммм, а як їх об’єднати , маючи це як цільове питання? Дублікат запитання має хороший заголовок, але він занадто довгий. Однак, це якось привернуло досить багато уваги пізно (я отримую кілька голосів за місяць ).
Fedorqui 'SO prestani шкодити'

@fedorqui Мені подобається концепція злиття, але я ніколи не бачив, щоб це відбувалося на практиці; якщо я правильно розумію ситуацію, моди просто не в змозі обробляти нетривіальні злиття, оскільки клопоти та складності значно переважають вигоди. Те, що я зробив один-два рази, - це видалити корисну та прихильну відповідь та перекласти її під інше питання, хоча навряд чи я можу рекомендувати це обхідне рішення.
триплі

Важко сказати, коли це варто робити. Я робив це на веб-сайті, який я модифікував, коли було опубліковано хороше запитання, не помітивши, що є ще одне гарне запитання раніше, обидва мають хороші відповіді. Видалення моєї відповіді з оцінкою 90+ не здається хорошим планом, оскільки це питання залишило б сироту.
Fedorqui 'SO припиніть шкоду'

20

Або, використовуючи ваші маркери EOF, вам потрібно вказати початковий маркер, щоб розширення не здійснювалося:

#-----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
Я б відредагував ваш допис, але щоб перестрахуватися, чи не це буде #! / Bin / bash, а не! / Bin / bash?
Метью Хогган,

@MatthewHoggan: Так, ти маєш рацію! Дякуємо, що зловили це. Зараз це виправляю.
обстріл

16

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

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. 

Використання наступного також працює.

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

Крім того, варто зазначити, що при використанні таких гередоків, як << EOFзаміщення та змінне розширення тощо. Отже, робимо щось подібне:

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

завжди призведе до розширення змінних $HOMEта $PWD. Так що, якщо ваш домашній каталог /home/foobarі поточний шлях /home/foobar/bin, fileбуде виглядати наступним чином :

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

замість очікуваного:

cd "$HOME"
echo "$PWD"

2
Рядок тут в одинарних лапках, очевидно, не може містити жодних одинарних лапок, що може бути надмірною проблемою. Тут документи є єдиним розумним рішенням, якщо у вас є сценарій, який повинен містити як одинарні, так і подвійні лапки, хоча це, звичайно, не простий приклад OP. Крім того, дотично, тут-рядки <<<доступні лише починаючи з Bash 3, а не переносяться до інших оболонок.
триплі

<<<також доступний у Zsh
Олексій Магура

3
сьогодні я дізнався, що ви можете отримати ім'я файлу одразу після відкриття гередоку. Дякую @AlexejMagura!
chaseadamsio
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.