Як призначити значення змінної 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.