Як призначити значення змінної heredoc змінній у Bash?


374

У мене є ця рядкова рядок (цитати включені):

abc'asdf"
$(dont-execute-this)
foo"bar"''

Як я можу призначити його змінної за допомогою heredoc в Bash?

Мені потрібно зберегти нові рядки.

Я не хочу уникати символів у рядку, це буде дратувати ...


@JohnM - Я щойно спробував призначити гередок з одноцитатами 'EOF', з уникнутими ` in the content: if the second line has рядковими розривами командою cd`, я повертаюся: " .sh: рядок X: cd: команда не знайдена "; але якщо я подвійно цитую "EOF"; тоді змінні bash ${A}не зберігаються як рядки (вони розширюються); але потім, лінія-брейки будуть збережені - і я не маю проблеми запуску команди з cdу другому рядку ( і як «EOF» і «EOF» , здається, грають добре і з eval, для виконання набору команд , що зберігаються в струнна змінна ). Ура!
— sdaau

1
... і додати до мого попереднього коментаря: bash коментарі "#" у двокольоровій "EOF"змінній, якщо буде викликано через eval $VAR, призведе до коментування всіх решти сценарію, оскільки тут $ VAR буде розглядатися як один рядок ; щоб мати можливість використовувати #коментарі bash у мультилінійному сценарії, подвійне цитування також змінне в eval call: eval "$ VAR" `.
— sdaau

@sdaau: У мене були проблеми з evalцим методом, але я його не відслідковував, оскільки він був частиною якогось пакету, який містить evalдеякі змінні, визначені у його конфігураційному файлі. Повідомлення про помилку було: /usr/lib/network/network: eval: line 153: syntax error: unexpected end of file. Я просто перейшов на інше рішення.
— Голар Рамблар

Відповіді:


515

Ви можете уникнути марного використання catневідповідних цитат і краще обробляти це:

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

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

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

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

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Якщо замість цього ви хочете зберегти вкладки у вмісті отриманої змінної, вам потрібно видалити вкладку IFS. Кінцевий маркер для тут doc ( EOF) не повинен бути відступним.

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

Вкладки можна вставити в командний рядок, натиснувши Ctrl- V Tab. Якщо ви використовуєте редактор, залежно від того, який з них може також працювати, або можливо, вам доведеться вимкнути функцію, яка автоматично перетворює вкладки в пробіли.


117
Я думаю, що варто згадати, що якщо у вас є set -o errexit(ака set -e) у своєму скрипті, і ви користуєтесь цим, він припинить ваш сценарій, оскільки readповертає ненульовий код повернення, коли він досягне EOF.
— Марк Байєрс

14
@MarkByers: Це одна з причин, яку я ніколи не використовую set -eі завжди рекомендую проти її використання. Краще замість цього використовувати правильне керування помилками. trapтвій друг. Інші друзі: elseі ||серед інших.
— Призупинено до подальшого повідомлення.

7
Чи уникнення catсправді того варте в такому випадку? Призначення гередока змінній з cat- це добре відома ідіома. Якось використання readобфускує речей для невеликої користі.
— Григорій Пакош

6
@ulidtko Це тому, що у вас немає пробілу між dпорожнім рядком; bashобвали -rd''просто , -rdперш ніж readколи - або бачать свої аргументи, так VARтрактуються в якості аргументу -d.
— чепнер

6
У такому форматі readповернеться з ненульовим кодом виходу. Це робить цей метод менш ідеальним у сценарії з увімкненою перевіркою помилок (наприклад set -e).
— Швейцарія

245

Використовуйте $ () для присвоєння результату catвашій змінній так:

VAR=$(cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)

# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR

Не забудьте розмежувати старт END_HEREDOC за допомогою одинарних лапок.

Зауважте, що роздільник закінчення гередока END_HEREDOCповинен бути один на рядку (отже, кінцеві дужки - у наступному рядку).

Дякую @ephemientза відповідь.


1
Насправді, це дає змогу навести цитати за певних обставин. Мені вдалося це зробити в Перлі легко ...
— Ніл

32
+1. Це найбільш читабельне рішення, принаймні для моїх очей. Ім'я змінної залишає в крайній лівій частині сторінки, а не вбудовує її в команду read.
— Клейтон Стенлі

12
PSA: пам’ятайте, що змінну потрібно цитувати для збереження нових рядків. echo "$VAR"замість echo $VAR.
— sevko

7
Це добре ashі з OpenWRT, де readне підтримується -d.
— Девід Ерманн

3
З причин, які я не можу зрозуміти, це не вдається з помилкою "несподіваного EOF", якщо у гередока у вас є неспарений фон.
— Радон Росборо

79

це варіація методу Денніса, виглядає вишуканіше в сценаріях.

визначення функції:

define(){ IFS='\n' read -r -d '' ${1} || true; }

використання:

define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

echo "$VAR"

насолоджуватися

ps зробив версію "циклу читання" для оболонок, які не підтримують read -d. повинні працювати з set -euі непарних зворотні лапки , але не протестовані дуже добре:

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }

1
Здається, це працює лише поверхово. Функція define поверне статус 1, і я не зовсім впевнений, що потрібно виправити.
— fny

1
Це також перевершує прийняту відповідь, оскільки вона може бути модифікована для підтримки POSIX sh на додаток до bash ( readцикл у функції, щоб уникнути -d ''башизму, необхідного для збереження нових рядків).
— ELLIOTTCABLE

На відміну від опції cat-in-a-subshell, ця функція працює з непарними задумами в гередоці. Дякую!
— Радон Росборо

Це рішення працює з set -eнабором, тоді як обрана відповідь не відповідає. Здається, це черезhttp://unix.stackexchange.com/a/265151/20650
— примхливий

2
@fny ps статус повернення вже давно зафіксовано
— ttt

36
VAR=<<END
abc
END

не працює, тому що ви перенаправляєте stdin на те, що не хвилює цього, а саме на призначення

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

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

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A

Я оновив своє запитання, щоб включити $ (виконуваний файл). Крім того, як ви зберігаєте нові рядки?
— Ніл

2
@ l0st3d: Так близько ... Використовуйте $(cat <<'END'замість цього. @Neil: Останній новий рядок не буде частиною змінної, але решта збережеться.
— ефемія

1
Схоже, що будь-які нові рядки збереглися. Запуск вищенаведеного прикладу я бачу: "sdfsdf sdfsdf sdfsfds" ... ах! Але пишучи echo "$A"(тобто ставлячи $ A у подвійних лапках), і ви бачите нові рядки!
— Даррен Кук

1
@Darren: ага! Я помітив випуск нових рядків, і використання лапок навколо змінної виводу вирішує проблему. Дякую!
— javadba

1
Цікаво, що в зв'язку з волею першого прикладу, в крайньому випадку , ви можете використовувати його для саморобних блоків коментарів , як це: REM=<< 'REM' ... comment block goes here ... REM. Або більш компактно, : << 'REM' .... Де "REM" може бути чимось на зразок "ПРИМІТКИ" або "SCRATCHPAD" тощо.
— Beejor

34

Досі не існує рішення, яке б зберігало нові рядки.

Це неправда - ви, мабуть, просто введені в оману поведінкою ехо:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines


5
Дійсно, це поведінка того, як працює цитування змінної. Без лапок, вони вставлятимуть їх як різні параметри, простір деформується, тоді як з лапок весь вміст змінної буде розглядатися як один аргумент
— Czipperz

11

Відганяючи відповідь Ніла , вам часто взагалі не потрібен var, ви можете використовувати функцію так само, як і змінну, і її читати набагато простіше, ніж рішення вбудованого або readбазованого типу.

$ complex_message() {
  cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}

$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''

9

Масив є змінною, тому в такому випадку mapfile буде працювати

mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

Потім ви можете надрукувати так

printf %s "${y[@]}"

3

присвоїти змінній значення heredoc

VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"

використовується як аргумент команди

echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx  123123    123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"

Коли я спробував перший метод, здається, що між рядками немає термінаторів. Повинна бути якась конфігурація на моїй машині Linux?
— Kemin Zhou

Це, ймовірно, означає, що коли ви повторювали свою змінну, ви не ставили цитати навколо неї ... Спробуйте так:echo "$VAR"
— Бред Паркс

1

Мені здалося, що мені потрібно прочитати рядок з NULL в ньому, тому ось рішення, яке буде читати що завгодно ви кинете на нього. Хоча якщо ви насправді маєте справу з NULL, вам доведеться розібратися з цим на шестигранному рівні.

$ cat> read.dd.sh

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

Доказ:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

Приклад HEREDOC (з ^ J, ^ M, ^ I):

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE

0

Завдяки відповіді dimo414 , це показує, як працює його чудове рішення, і показує, що ви можете легко і змінні у тексті:

Приклад виведення

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main

Було б цікаво побачити незрівнянну цитату в тексті, щоб побачити, як це впорається з цим. Можливо, `Не вигадайте, є" $ COUNT "файли", тому апостроф / одна цитата може зробити цікаві речі.
— dragon788

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