Как катить << EOF >> файл, содержащий код?

102

Я хочу напечатать код в файле, используя 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

Я попытался поставить одинарные кавычки, но результат также содержит одинарные кавычки. Как я могу избежать этой проблемы?

UberNate
источник
2
Также следует исправить шебанг. Первая строка должна быть буквально #!/bin/bashи ничего больше - #!это то, что делает ее действительной строкой shebang, а то, что идет после нее, - это путь к интерпретатору.
tripleee
1
посмотреть man bashи искать Here Documents. Все подробности есть.
Гонконг
1
В качестве опоздания отметим, что современный синтаксис для подстановки процессов - $(command)вместо `command`. Для получения содержимого файла у Bash есть$(<file)
tripleee

Ответы:

160

Вам нужно только минимальное изменение; заключите в одинарные кавычки разделитель здесь-документа после <<.

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, stop harming'
@fedorqui Может быть, вы действительно хотите перенести свой ответ на этот вопрос? Или мы можем переключить дублирующую маркировку на обратное; Я просто смотрел на оценку вопроса, а не на ответ.
Tripleee 06
Ммм, а как насчет их объединения , имея этот вопрос в качестве целевого? Повторяющийся вопрос имеет хорошее название, только слишком длинный. Однако в последнее время он каким-то образом привлек довольно много внимания (я получаю несколько голосов в месяц ).
fedorqui `` SO, перестаньте причинять вред ''
@fedorqui Мне нравится концепция слияния, но я никогда не видел, чтобы это происходило на практике; Если я правильно понимаю ситуацию, моды просто не могут обрабатывать нетривиальные слияния, потому что хлопоты и сложности намного перевешивают преимущества. Что я сделал один или два раза, так это удалил полезный ответ, за который проголосовали, и разместил его под другим вопросом, хотя я вряд ли могу рекомендовать этот обходной путь.
Tripleee 06
Трудно сказать, когда это стоит делать. Я сделал это на сайте, который я модифицирую, когда был опубликован хороший вопрос, не замечая, что был еще один хороший, и оба имели хорошие ответы. Удаление моего ответа с оценкой 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: Ага, ты прав! Спасибо, что уловили это. Чиню сейчас.
shellter
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 ...'

Также стоит отметить, что при использовании heredocs, таких как << 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 и не переносятся в другие оболочки.
Tripleee
<<<также доступно на Zsh
Alexej Magura
3
сегодня я узнал, что вы можете иметь имя файла сразу после открытия heredoc. Спасибо @AlexejMagura!
chaseadamsio
0

Я знаю, что это вопрос двухлетней давности, но это быстрый ответ для тех, кто ищет «как».

Если вы не хотите заключать что-либо в кавычки, вы можете просто записать блок текста в файл и экранировать переменные, которые вы хотите экспортировать как текст (например, для использования в скрипте), и не экранировать те, которые вы хотите экспорт как значение переменной.

#!/bin/bash

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

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

Запишет "$ {VAR_EXAMPLE} =" string "in test.txt" в test.txt

Это также можно использовать для вывода блоков текста на консоль по тем же правилам, опуская имя файла.

#!/bin/bash

VAR_EXAMPLE="\"string\""

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

Будет выводить "$ {VAR_EXAMPLE} =" string "to console" на консоль

GCUGreyArea
источник