Как присвоить значение heredoc переменной в Bash?


У меня есть эта многострочная строка (включая кавычки):

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

Как бы я назначил его переменной, используя heredoc в Bash?

мне нужно сохранить новые строки.

Я не хочу, чтобы избежать символов в строке, что было бы раздражает...

11 274

11 ответов:

вы можете избежать бесполезного использования cat и обрабатывать несоответствующие цитаты лучше с этим:

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

если вы не цитируете переменную, когда вы повторяете ее, новые строки теряются. Цитирование его сохраняет их:

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

если вы хотите использовать отступ для удобства чтения в исходном коде, используйте тире после менее-thans. Отступ должен быть выполнен только с помощью вкладок (без пробелов).

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

если вместо этого вы хотите сохранить вкладки в содержимом результирующая переменная, вам нужно удалить вкладку из IFS. Маркер терминала для here 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 -VTab. Если вы используете редактор, в зависимости от того, какой из них, это также может работать, или вам может потребоваться отключить функцию, которая автоматически преобразует вкладки в пробелы.

используйте $ (), чтобы назначить выход 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 с одинарными.

обратите внимание, что окончание heredoc разделитель END_HEREDOC должен быть один на линии (следовательно, конечная скобка находится на следующей строке).

спасибо @ephemient для ответа.

Это вариация метода Денниса, выглядит более элегантно в скриптах.

функции определение:

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

использование:

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

echo "$VAR"

наслаждайтесь

p. s. made a 'read loop' версия для оболочек, которые не поддерживают read -d. должен работать с set -eu и непарные обратные кавычки, но не очень хорошо проверено:

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "=$o"; }
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

добавление комментария здесь в качестве ответа, так как у меня нет достаточно точек повторения, чтобы прокомментировать ваш текст вопроса.

по-прежнему нет решения, которое сохраняет новые строки.

это не так - вы, вероятно, просто вводится в заблуждение поведением echo:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines

массив является переменной, поэтому в этом случае mapfile будет работать

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

тогда вы можете печатать так

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

ответвляется Нил, вам часто не нужен 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"''

присвоить значение 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
)"

я обнаружил, что мне нужно прочитать строку с нулем в ней, так что вот решение, которое будет читать что-нибудь вы бросаете на нее. Хотя, если вы действительно имеете дело с 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

спасибо ответ 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

   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
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT