Чистий спосіб записати складний багаторядковий рядок до змінної


109

Мені потрібно написати якийсь складний xml до змінної всередині bash-скрипту. XML має бути читабельним у скрипті bash, оскільки тут буде жити фрагмент xml, він не читається з іншого файлу чи джерела.

Отже, моє запитання полягає в тому, якщо у мене є довга струна, яку я хочу бути читаною людиною в моєму баш-скрипті, який найкращий шлях для цього?

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

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

Чи можна це зробити за допомогою 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

Я готовий зробити ставку, що ви просто збираєтеся знову скинути ці дані в потік. Навіщо зберігати його у змінній, коли ви могли зробити складніші речі та використовувати потоки?
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").
Денніс Вільямсон

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}"

На жаль, апостроф у "Рафаелі" змушує першого не працювати.
Денніс Вільямсон

Обидві завдання з часом працюють для мене. Єдина цитата в VAR1 не повинна бути проблемою (принаймні, не для баш). Можливо, вас ввели в оману підсвічуванням синтаксису?
joschi

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


1
+1 це рішення дозволяє
змінити

Вгору: сумісність sh. Нижня сторона: задній план призупиняється / перешкоджає баш. Тепер якби мені довелося вибирати між ш та баш ...
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 .

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