Maneira limpa de escrever uma sequência complexa de várias linhas em uma variável

109

Eu preciso escrever um xml complexo para uma variável dentro de um script bash. O xml precisa ser legível dentro do script bash, pois é onde o fragmento xml ficará, não está sendo lido em outro arquivo ou fonte.

Então, minha pergunta é: se eu tiver uma longa string que eu queira ser legível por humanos dentro do meu script bash, qual é a melhor maneira de fazer isso?

Idealmente eu quero:

  • para não ter que escapar de nenhum dos personagens
  • tê-lo atravessar várias linhas, tornando-o legível por humanos
  • mantenha o recuo

Isso pode ser feito com EOF ou algo assim, alguém poderia me dar um exemplo?

por exemplo

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
ChrisInCambo
fonte
Estou disposto a apostar que você só vai despejar esses dados em um fluxo novamente. Por que armazená-lo em uma variável quando você pode tornar as coisas mais complexas e usar fluxos?
Zenexer

Respostas:

140

Isso colocará seu texto em sua variável sem precisar escapar das aspas. Ele também manipulará aspas desequilibradas (apóstrofos, ie '). Colocar aspas no sentinela (EOF) impede que o texto sofra expansão de parâmetros. As -d''causas para ler várias linhas (ignorar novas linhas). readé um Bash embutido, portanto não requer a chamada de um comando externo, como 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
Dennis Williamson
fonte
17
+1 para evitar cat.
James Sneeringer
4
caté um comando externo. Não usá-lo economiza fazendo isso. Além disso, alguns têm a filosofia de que se você estiver usando gato com menos de dois argumentos "Você está fazendo errado" (que é diferente de "uso inútil de cat").
Dennis Williamson
9
e nunca travessão segunda EOF .... (tabela múltipla de cabeça bangs envolvidos)
IljaBek
9
Eu tentei usar a declaração acima enquanto set -e. Parece readsempre retornar diferente de zero. Você pode grossa esse comportamento usando! read -d .......
krissi
11
E se você estiver usando essa Stringvariável de várias linhas para gravar em um arquivo, coloque a variável em torno de "QUOTES" como echo "${String}" > /tmp/multiline_file.txtou echo "${String}" | tee /tmp/multiline_file.txt. Levei mais de uma hora para encontrar isso.
Aditya
28

Você já esteve quase lá. Você usa cat para a montagem da sua string ou cita a string inteira (nesse caso, você teria que escapar das aspas dentro da string):

#!/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}"
joschi
fonte
Infelizmente, o apóstrofo em "Raphael's" faz com que o primeiro não funcione.
Dennis Williamson
Ambas as tarefas funcionam para mim eventualmente. As aspas simples no VAR1 não devem ser um problema (pelo menos não para o bash). Talvez você tenha sido enganado pelo destaque da sintaxe?
Joschi
1
Funciona em um script, mas não em um prompt do Bash. Desculpe por não ser mais claro.
Dennis Williamson
1
É melhor citar o EOF como 'EOF'ou "EOF", caso contrário, as variáveis ​​do shell serão analisadas.
Stanislav German-Evtushenko
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}"

Isso deve funcionar bem no ambiente de shell Bourne

schweiz
fonte
1
Marque com
De cabeça: compatível com sh. Desvantagem: os backticks são preteridos / desencorajados no bash. Agora, se eu tivesse que escolher entre sh e bash ...
Zenexer 26/09
2
desde quando os backticks são preteridos / desencorajados? apenas curioso
Alexander Mills
6

Mais uma maneira de fazer o mesmo ...

Eu gosto de usar variáveis ​​e especiais <<-que eliminam a tabulação no início de cada linha para permitir o recuo do script:

#!/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

aviso : não há espaço em branco antes, eofmas apenas tabulação .

<?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>
Algumas explicações:
  • O mapfile lê todo o documento aqui em uma matriz.
  • a sintaxe "${Pattern[*]}"lança esse array em uma string.
  • Eu uso IFS=";"porque não há ;nas strings necessárias
  • A sintaxe while IFS=";" read file ...impede IFSque seja modificado para o restante do script. Neste, readuse apenas o modificado IFS.
  • sem garfo.
F. Hauri
fonte
Observe que mapfilerequer o Bash 4 ou superior. E a sintaxe "${Pattern[*]}"lança a matriz em uma cadeia de caracteres quando estiver entre aspas (como mostrado no código de exemplo).
Dennis Williamson
Sim, o bash 4 era muito novo quando essa pergunta foi feita.
F. Hauri
2

Existem muitos casos de canto em muitas das outras respostas.

Para ter certeza absoluta de que não há problemas com espaços, guias, IFS etc., uma abordagem melhor é usar a construção "heredoc", mas codifique o conteúdo do heredoc usando uuencodeo explicado aqui:

https://stackoverflow.com/questions/6896025/#11379627 .

Chris Johnson
fonte