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


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.