Como gerar uma sequência de linhas multilinhas no Bash?

250

Como posso gerar uma sequência de linhas multipline no Bash sem usar várias chamadas de eco da seguinte forma:

echo "usage: up [--level <n>| -n <levels>][--help][--version]"
echo 
echo "Report bugs to: "
echo "up home page: "

Estou procurando uma maneira portátil de fazer isso, usando apenas os recursos do Bash.

helpermethod
fonte
4
Se você estiver saída a mensagem de uso em resposta a uma invocação incorreta, você normalmente enviar essa mensagem para o erro padrão em vez de saída padrão, comecho >&2 ...
Mark Reed
2
@MarkReed A mensagem de uso é exibida digitando --help(que deve ir para a saída padrão).
Helpermethod
Para outros que aparecerem, mais informações sobre "documentos aqui" estão disponíveis: tldp.org/LDP/abs/html/here-docs.html
Jeffrey Martinez
Verifique a printfsolução baseada em Gordon Davidson. Apesar de estar na sombra das abordagens echoou com catbase, parece ser muito menos um argumento. É certo que o `printf' sintaxe representam um pouco de uma curva de aprendizado, mas eu gostaria de ouvido de outros inconvenientes (compatibilidade, desempenho ...?)
mjv
Relacionados: stackoverflow.com/questions/23929235/...
Anton Tarasenko

Respostas:

296

Aqui, os documentos são frequentemente usados ​​para esse fim.

cat << EOF
usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page:
EOF

Eles são suportados em todos os shells derivados de Bourne, incluindo todas as versões do Bash.

Pausado até novo aviso.
fonte
4
Sim - mas catnão é um built-in.
Mark Reed
8
@ MarkReed: Isso é verdade, mas está sempre disponível (exceto possivelmente em circunstâncias incomuns).
Pausado até novo aviso.
6
+1 Thx. Acabei usando read -d '' help <<- EOF ...para ler a sequência multilinha em uma variável e, em seguida, repeti o resultado.
helpermethod
3
posso salvar um HEREDOC em uma variável?
chovy
177

ou você pode fazer isso:

echo "usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page: "
Chris Mohr
fonte
1
@OliverWeiler: Funcionará até em cartuchos Bourne, como Dash e Bourne Shell, da Heirloom .
Pausado até novo aviso.
6
Não é bom se você precisar disso em uma função, pois precisará: 1) recuar a string até a esquerda do arquivo ou 2) mantê-la recuada para alinhar-se com o restante do código, mas depois será impressa com os recuos também
sg
43

Inspirado pelas respostas perspicazes desta página, criei uma abordagem mista, que considero a mais simples e mais flexível. O que você acha?

Primeiro, defino o uso em uma variável, o que me permite reutilizá-lo em diferentes contextos. O formato é muito simples, quase WYSIWYG, sem a necessidade de adicionar nenhum caractere de controle. Isso me parece razoavelmente portátil (eu o executei no MacOS e Ubuntu)

__usage="
Usage: $(basename $0) [OPTIONS]

Options:
  -l, --level <n>              Something something something level
  -n, --nnnnn <levels>         Something something something n
  -h, --help                   Something something something help
  -v, --version                Something something something version
"

Então eu posso simplesmente usá-lo como

echo "$__usage"

ou, melhor ainda, ao analisar parâmetros, posso repeti-lo em uma única linha:

levelN=${2:?"--level: n is required!""${__usage}"}
Jorge
fonte
4
isso funcionou para mim em um script em que a resposta acima não funciona (sem modificação).
David Welch
3
Isso é muito mais limpo do que envolver um monte de caracteres como \ te \ n que são difíceis de encontrar no texto e expandir para tornar a saída muito diferente da string no script
sg
1
Por alguns motivos, ele imprime tudo na mesma linha para mim: /
Nicolas de Fontenay
2
@ Nicolas: Usar aspas duplas echo "$__usage"era necessário para mim. echo $__usagenão funcionou.
Mario
24

Use a -eopção e, em seguida, você pode imprimir um novo caractere de linha \nna sequência.

Amostra (mas não tenho certeza se uma boa ou não)

O engraçado é que essa -eopção não está documentada na página do manual do MacOS enquanto ainda é utilizável. Está documentado na página de manual do Linux .

nhahtdh
fonte
6
Essas páginas de manual são para o echocomando fornecido pelo sistema /bin/echo, que no Mac OS não tem -eopção. quando você está usando o bash nesses sistemas, seu echocomando interno assume o controle. Você pode ver isso digitando /bin/echo whatevere observando explicitamente a diferença de comportamento. Para ver a documentação do interno, digite help echo.
Mark Reed
1
/bin/echogeralmente é diferente de um sistema operacional para outro e diferente do built-in do Bash echo.
Pausado até novo aviso.
@ MarkReed: Vou tentar mais tarde, mas obrigado pela informação. +1. Vou deixar minha resposta aqui, pois há muita discussão em andamento.
N
7
echo -enão é portátil - por exemplo, algumas implementações de eco imprimirão o "-e" como parte da saída. Se você deseja portabilidade, use printf. Por exemplo, / bin / echo no OS X 10.7.4 faz isso. O IIRC, o eco interno do bash, também era estranho no 10.5.0, mas não me lembro mais dos detalhes.
Gordon Davisson
2
echo -ejá me mordeu antes ... Definitivamente, use printfou catcom um heredoc. A <<-variante daqui docs são especialmente bom porque você pode tirar recuo líder na saída, mas travessão para facilitar a leitura no script
zbeekman
22

Como recomendei printfem um comentário, provavelmente eu deveria dar alguns exemplos de seu uso (embora para imprimir uma mensagem de uso, seria mais provável que eu usasse as respostas de Dennis ou Chris). printfé um pouco mais complexo de usar do que echo. Seu primeiro argumento é uma string de formato, na qual escapes (como \n) são sempre interpretados; ele também pode conter diretivas de formato começando com %, que controlam onde e como quaisquer argumentos adicionais são incluídos nela. Aqui estão duas abordagens diferentes para usá-lo para uma mensagem de uso:

Primeiro, você pode incluir a mensagem inteira na string de formato:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: \nup home page: \n"

Observe que echo, ao contrário , você deve incluir explicitamente a nova linha final. Além disso, se a mensagem contiver algum %caractere, ele deverá ser escrito como %%. Se você deseja incluir o relatório de erros e os endereços da página inicial, eles podem ser adicionados naturalmente:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: %s\nup home page: %s\n" "$bugreport" "$homepage"

Segundo, você pode usar a string de formato para imprimir cada argumento adicional em uma linha separada:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: " "up home page: "

Com esta opção, adicionar o relatório de erros e os endereços da página inicial é bastante óbvio:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: $bugreport" "up home page: $homepage"
Gordon Davisson
fonte
9

Também com o código-fonte recuado, você pode usar <<-(com um traço à direita) para ignorar as guias principais (mas não os espaços iniciais). Por exemplo, isto:

if [ some test ]; then
    cat <<- xx
        line1
        line2
xx
fi

Produz texto recuado sem o espaço em branco à esquerda:

line1
line2
Vista elíptica
fonte
Isso não funcionou para mim. Qual shell você está usando?
four43
Não funcionou no bash 4.4.19 no Ubuntu. Ele não remover o espaçamento antes line1 e line2
four43
1
@ four43, você estava certo. Não funciona para remover espaços à esquerda. No entanto, remove as guias principais. Portanto, corrigi minha resposta de abas e espaços, abas e não espaços. Desculpe-me pelo erro. Eu verifiquei o manual e claramente diz apenas que as guias são removidas. Obrigado por trazer isso à minha atenção.
Visualização elíptica
0

Se você usar a solução de @jorge e descobrir que tudo está na mesma linha, coloque a variável entre aspas:

echo $__usage

imprimirá tudo em uma linha enquanto

echo "$__usage"

manterá as novas linhas.

user1165471
fonte
Esta é realmente a única solução que funcionou para mim. O Printf faz muitas coisas e, com o meu xml de várias linhas, provavelmente requer muita fuga, porque gerencia completamente o conteúdo. cat <<EOF .... EOF
Atribuo