Linhas de continuação do Bash

158

Como você usa linhas de continuação do bash?

Sei que você pode fazer isso:

echo "continuation \
lines"
>continuation lines

No entanto, se você tiver um recuo do código, ele não funcionará tão bem:

    echo "continuation \
    lines"
>continuation     lines
PyRulez
fonte
5
O Guia de estilos do Bash Shell do Google recomenda documentos "aqui" para "Se você precisar escrever strings com mais de 80 caracteres". Veja a resposta do @ tripleee .
Trevor Boyd Smith
google.github.io/styleguide/… - este é o link direto no novo documento
jcollum 26/03

Respostas:

161

Isto é o que você pode querer

$       echo "continuation"\
>       "lines"
continuation lines

Se isso cria dois argumentos para ecoar e você deseja apenas um, vamos dar uma olhada na concatenação de strings. No bash, a colocação de duas cadeias uma ao lado da outra concatena:

$ echo "continuation""lines"
continuationlines

Portanto, uma linha de continuação sem recuo é uma maneira de quebrar uma string:

$ echo "continuation"\
> "lines"
continuationlines

Mas quando um recuo é usado:

$       echo "continuation"\
>       "lines"
continuation lines

Você recebe dois argumentos porque isso não é mais uma concatenação.

Se você deseja uma única string que cruza as linhas, enquanto recua, mas não obtém todos esses espaços, uma abordagem que você pode tentar é abandonar a linha de continuação e usar variáveis:

$ a="continuation"
$ b="lines"
$ echo $a$b
continuationlines

Isso permitirá que você tenha um código recuado de maneira limpa à custa de variáveis ​​adicionais. Se você tornar as variáveis ​​locais, não deve ser tão ruim.

Ray Toal
fonte
1
Obrigado por sua ajuda, mas, embora isso remova os espaços, agora eles são parâmetros separados (o Bash está interpretando os espaços na segunda linha como um separador de parâmetros) e agora são impressos apenas corretamente devido ao comando echo.
1
Ah, você gostaria de uma única string (bash) para abranger linhas! Eu vejo agora.
quer
4
Solução com uma variável:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman
30

Aqui, os documentos com o <<-HEREterminador funcionam bem para cadeias de texto de múltiplas linhas recuadas. Ele removerá todas as guias principais do documento aqui. (Os terminadores de linha ainda permanecerão.)

cat <<-____HERE
    continuation
    lines
____HERE

Consulte também http://ss64.com/bash/syntax-here.html

Se você precisar preservar alguns, mas não todos, espaços em branco à esquerda, use algo como

sed 's/^  //' <<____HERE
    This has four leading spaces.
    Two of them will be removed by sed.
____HERE

ou talvez use trpara se livrar das novas linhas:

tr -d '\012' <<-____
    continuation
     lines
____

(A segunda linha tem uma aba e um espaço na frente; a aba será removida pelo operador do traço antes do terminador heredoc, enquanto o espaço será preservado.)

Para agrupar seqüências longas e complexas em várias linhas, eu gosto de printf:

printf '%s' \
    "This will all be printed on a " \
    "single line (because the format string " \
    "doesn't specify any newline)"

Também funciona bem em contextos em que você deseja incorporar partes não triviais do shell script em outro idioma em que a sintaxe do idioma host não permita que você use um documento aqui, como em um Makefileou Dockerfile.

printf '%s\n' >./myscript \
    '#!/bin/sh` \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript
triplo
fonte
Não funciona para mim. Ubuntu 16.04. Recebo duas linhas em vez da esperada concatenada.
Penghe Geng
@PengheGeng De fato, isso resolve o problema de se livrar do recuo, não o de unir linhas. Você ainda pode inverter a barra invertida no final de uma linha para unir duas linhas.
Tripleee
(Mas veja o primeiro printfexemplo também agora.)
tripleee
12

Você pode usar matrizes bash

$ str_array=("continuation"
             "lines")

então

$ echo "${str_array[*]}"
continuation lines

existe um espaço extra, porque (após o manual do bash):

Se a palavra estiver entre aspas duplas, ${name[*]}expande para uma única palavra com o valor de cada membro da matriz separado pelo primeiro caractere da variável IFS

Então IFS=''prepare-se para se livrar do espaço extra

$ IFS=''
$ echo "${str_array[*]}"
continuationlines
Tworec
fonte
4

Em certos cenários, a utilização da capacidade de concatenação do Bash pode ser apropriada.

Exemplo:

temp='this string is very long '
temp+='so I will separate it onto multiple lines'
echo $temp
this string is very long so I will separate it onto multiple lines

Na seção PARÂMETROS da página do Bash Man:

nome = [valor] ...

... No contexto em que uma instrução de atribuição está atribuindo um valor a uma variável de shell ou índice de matriz, o operador + = pode ser usado para acrescentar ou adicionar ao valor anterior da variável. Quando + = é aplicado a uma variável para a qual o atributo inteiro foi definido, o valor é avaliado como uma expressão aritmética e adicionado ao valor atual da variável, que também é avaliado. Quando + = é aplicado a uma variável de matriz usando atribuição composta (consulte Matrizes abaixo), o valor da variável não é desmarcado (como é o caso de =) e novos valores são acrescentados à matriz, começando com um valor maior que o índice máximo da matriz (para matrizes indexadas) ou adicionadas como pares de valores-chave adicionais em uma matriz associativa. Quando aplicado a uma variável com valor de sequência, o valor é expandido e anexado ao valor da variável.

Cybernaut
fonte
Provavelmente o melhor conselho nesta página. Usar um HEREDOC e canalizar para uma conversão para <CR> s é muito pouco intuitivo e falha quando a string realmente precisa ter separadores de linha colocados discretamente.
ingyhere 13/01
2

Me deparei com uma situação em que eu tinha que enviar uma mensagem longa como parte de um argumento de comando e tinha que aderir à limitação do comprimento da linha. Os comandos são mais ou menos assim:

somecommand --message="I am a long message" args

A maneira que resolvi isso é mover a mensagem como um documento aqui (como o @tripleee sugeriu). Mas um documento aqui se torna um stdin, portanto, ele precisa ser lido novamente. Fiz a seguinte abordagem:

message=$(
    tr "\n" " " <<- END
        This is a
        long message
END
)
somecommand --message="$message" args

Isso tem a vantagem de $messagepoder ser usado exatamente como a constante da string, sem espaços em branco ou quebras de linha extras.

Observe que as linhas de mensagem reais acima são prefixadas com um tabcaractere cada, que é excluído pelo próprio documento aqui (devido ao uso de <<-). Ainda existem quebras de linha no final, que são substituídas por ddespaços.

Observe também que, se você não remover novas linhas, elas aparecerão como estão quando "$message"forem expandidas. Em alguns casos, você pode solucionar o problema removendo as aspas duplas $message, mas a mensagem não será mais um argumento único.

haridsv
fonte
2

As continuações de linha também podem ser obtidas através do uso inteligente da sintaxe.

No caso de echo:

# echo '-n' flag prevents trailing <CR> 
echo -n "This is my one-line statement" ;
echo -n " that I would like to make."
This is my one-line statement that I would like to make.

No caso de vars:

outp="This is my one-line statement" ; 
outp+=" that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Outra abordagem no caso de vars:

outp="This is my one-line statement" ; 
outp="${outp} that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Voila!

aqui
fonte
1

Você pode simplesmente separá-lo com novas linhas (sem usar barra invertida) conforme necessário dentro do recuo da seguinte forma e apenas retirar novas linhas.

Exemplo:

echo "continuation
of 
lines" | tr '\n' ' '

Ou, se for uma definição variável, as novas linhas são convertidas automaticamente em espaços. Portanto, retire os espaços extras somente se aplicável.

x="continuation
of multiple
lines"
y="red|blue|
green|yellow"

echo $x # This will do as the converted space actually is meaningful
echo $y | tr -d ' ' # Stripping of space may be preferable in this case
rjni
fonte
1

Isso não é exatamente o que o usuário pediu, mas outra maneira de criar uma cadeia longa que abrange várias linhas é construindo-a de forma incremental, da seguinte forma:

$ greeting="Hello"
$ greeting="$greeting, World"
$ echo $greeting
Hello, World

Obviamente, nesse caso, teria sido mais simples construí-lo de uma só vez, mas esse estilo pode ser muito leve e compreensível ao lidar com cordas mais longas.

Jon McClung
fonte
0

No entanto, se você tiver um recuo do código, ele não funcionará tão bem:

    echo "continuation \
    lines"
>continuation     lines

Tente com aspas simples e concatenar as strings:

    echo 'continuation' \
    'lines'
>continuation lines

Nota: a concatenação inclui um espaço em branco.

mMontu
fonte
2
Ele funciona com argumentos de eco e string, mas não funciona com outras coisas, como atribuição de variável. Embora a pergunta não fosse sobre variáveis, usar eco era apenas um exemplo. Em vez de echo se você tivesse x=, você iria obter o erro: lines: command not found.
LS
0

Dependendo do tipo de risco que você aceitará e de quanto conhece e confia nos dados, é possível usar a interpolação simplista de variáveis.

$: x="
    this
    is
       variably indented
    stuff
   "
$: echo "$x" # preserves the newlines and spacing

    this
    is
       variably indented
    stuff

$: echo $x # no quotes, stacks it "neatly" with minimal spacing
this is variably indented stuff
Paul Hodges
fonte
-2

Provavelmente, isso realmente não responde à sua pergunta, mas você pode achar útil de qualquer maneira.

O primeiro comando cria o script que é exibido pelo segundo comando.

O terceiro comando torna esse script executável.

O quarto comando fornece um exemplo de uso.

john@malkovich:~/tmp/so$ echo $'#!/usr/bin/env python\nimport textwrap, sys\n\ndef bash_dedent(text):\n    """Dedent all but the first line in the passed `text`."""\n    try:\n        first, rest = text.split("\\n", 1)\n        return "\\n".join([first, textwrap.dedent(rest)])\n    except ValueError:\n        return text  # single-line string\n\nprint bash_dedent(sys.argv[1])'  > bash_dedent
john@malkovich:~/tmp/so$ cat bash_dedent 
#!/usr/bin/env python
import textwrap, sys

def bash_dedent(text):
    """Dedent all but the first line in the passed `text`."""
    try:
        first, rest = text.split("\n", 1)
        return "\n".join([first, textwrap.dedent(rest)])
    except ValueError:
        return text  # single-line string

print bash_dedent(sys.argv[1])
john@malkovich:~/tmp/so$ chmod a+x bash_dedent
john@malkovich:~/tmp/so$ echo "$(./bash_dedent "first line
>     second line
>     third line")"
first line
second line
third line

Observe que, se você realmente deseja usar esse script, faz mais sentido mover o script executável ~/binpara que ele fique no seu caminho.

Verifique a referência do python para obter detalhes sobre como textwrap.dedentfunciona.

Se o uso de $'...'ou "$(...)"for confuso para você, faça outra pergunta (uma por construção), se ainda não houver uma. Pode ser bom fornecer um link para a pergunta que você encontra / faz, para que outras pessoas tenham uma referência vinculada.

intuído
fonte
6
Bem-intencionado - e possivelmente até útil -, o OP solicitou conselhos sobre sintaxe básica do bash, e você forneceu a ele uma definição de função python que usa paradigmas de OO, controle excepcional de fluxo e importações. Além disso, você chamou um executável como parte de uma interpolação de string - algo que uma pessoa que faz esse tipo de pergunta definitivamente ainda não teria visto no bash.
Tiro parta