Чистый способ записи сложной многострочной строки в переменную

109

Мне нужно написать несколько сложных XML в переменную внутри скрипта bash. Xml должен быть читаемым внутри скрипта bash, так как именно здесь будет находиться фрагмент xml, он не читается из другого файла или источника.

Итак, мой вопрос заключается в следующем: если у меня есть длинная строка, которую я хочу, чтобы в моем скрипте bash читалось человеком, как лучше всего это сделать?

В идеале я хочу:

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

Можно ли это сделать с помощью EOF или еще чего-нибудь, кто-нибудь может дать мне пример?

например

String = <<EOF
 <?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF
ChrisInCambo
источник
Готов поспорить, что вы просто снова выбросите эти данные в поток. Зачем хранить это в переменной, если вы можете сделать вещи более сложными и использовать потоки?
Zenexer

Ответы:

140

Это поместит ваш текст в вашу переменную без необходимости экранировать кавычки. Он также будет обрабатывать несбалансированные кавычки (то есть апострофы '). Помещение кавычек вокруг часового (EOF) предотвращает расширение параметра в тексте. В -d''причинах его прочитать несколько строк (игнорировать символ новой строки). readявляется встроенным в Bash, поэтому не требует вызова внешней команды, такой как cat.

IFS='' read -r -d '' String <<"EOF"
<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF
Деннис Уильямсон
источник
17
+1 за уклонение cat.
Джеймс Снерингер
4
catэто внешняя команда. Не использовать это спасает делать это. Плюс, у некоторых есть философия, что если вы используете cat с менее чем двумя аргументами «Ура, вы делаете это неправильно» (что отличается от «бесполезного использования cat»).
Деннис Уильямсон
9
и никогда не сдвигайте второй EOF .... (задействовано несколько ударов по столу)
IljaBek
9
Я пытался использовать вышеупомянутое утверждение, пока set -e. Кажется, readвсегда возвращает ненулевое значение. Вы можете ! read -d .......
усилить
11
И если вы используете эту многострочную Stringпеременную для записи в файл, поместите переменную вокруг «QUOTES», как echo "${String}" > /tmp/multiline_file.txtили echo "${String}" | tee /tmp/multiline_file.txt. Мне потребовалось больше часа, чтобы найти это.
Адитья,
28

Вы были почти там. Либо вы используете cat для сборки вашей строки, либо заключаете в кавычки всю строку (в этом случае вам нужно будет избегать кавычек внутри вашей строки):

#!/bin/sh
VAR1=$(cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
)

VAR2="<?xml version=\"1.0\" encoding='UTF-8'?>
<painting>
  <img src=\"madonna.jpg\" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's \"Foligno\" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>"

echo "${VAR1}"
echo "${VAR2}"
Joschi
источник
К сожалению, апостроф в "Рафаэле" заставляет первый не работать.
Деннис Уильямсон
Оба задания в конечном итоге работают для меня. Одиночная кавычка в VAR1 не должна быть проблемой (по крайней мере, для bash). Может быть, вы были введены в заблуждение подсветкой синтаксиса?
Йоски
1
Это работает в сценарии, но не в приглашении Bash. Извините, что не яснее.
Деннис Уильямсон
1
Лучше заключать EOF в кавычки как 'EOF'или "EOF", иначе переменные оболочки будут проанализированы.
Станислав Герман-Евтушенко
13
#!/bin/sh

VAR1=`cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
`
echo "VAR1: ${VAR1}"

Это должно хорошо работать в среде оболочки Bourne

Schweiz
источник
1
+1 это решение допускает подстановку переменных, например, $ {foo}
Offirmo
Верх: sh-совместимый. Недостаток: обратные галочки не рекомендуется / не рекомендуется в bash. Теперь, если бы мне пришлось выбирать между sh и bash ...
Zenexer
2
с каких пор обратная связь не рекомендуется? просто любопытно
Александр Миллс
6

Еще один способ сделать то же самое ...

Мне нравится использовать переменные и специальные, <<-которые сбрасывают табуляцию в начале каждой строки, чтобы разрешить отступ скрипта:

#!/bin/bash

mapfile Pattern <<-eof
        <?xml version="1.0" encoding='UTF-8'?>
        <painting>
          <img src="%s" alt='%s'/>
          <caption>%s, painted in
          <date>%s</date>-<date>%s</date>.</caption>
        </painting>
        eof

while IFS=";" read file alt caption start end ;do
    printf "${Pattern[*]}" "$file" "$alt" "$caption" "$start" "$end"
  done <<-eof
        madonna.jpg;Foligno Madonna, by Raphael;This is Raphael's "Foligno" Madonna;1511;1512
        eof

предупреждение : раньше не было пустого места,eof а только табуляция .

<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
Некоторые объяснения:
  • mapfile прочитайте весь документ здесь в массиве.
  • синтаксис приведёт "${Pattern[*]}"этот массив в строку.
  • Я использую, IFS=";"потому что нет ;в обязательных строках
  • Синтаксис while IFS=";" read file ...не IFSпозволяет изменять остальную часть сценария. В этом только readиспользуйте измененный IFS.
  • нет вилки.
Ф. Хаури
источник
Обратите внимание, что mapfileтребуется Bash 4 или выше. И синтаксис "${Pattern[*]}"преобразует массив в строку в кавычках (как показано в примере кода).
Деннис Уильямсон,
Да, bash 4 был очень новым, когда был задан этот вопрос.
Ф. Хаури
2

Во многих других ответах слишком много угловых случаев.

Чтобы быть абсолютно уверенным, что нет проблем с пробелами, табуляциями, IFS и т. Д., Лучше использовать конструкцию «heredoc», но закодировать содержимое heredoc, uuencodeкак описано здесь:

https://stackoverflow.com/questions/6896025/#11379627 .

Крис Джонсон
источник