Líneas de continuación Bash


158

¿Cómo se usan las líneas de continuación de bash?

Me doy cuenta de que puedes hacer esto:

echo "continuation \
lines"
>continuation lines

Sin embargo, si tiene un código sangrado, no funciona tan bien:

    echo "continuation \
    lines"
>continuation     lines

55
La Guía de estilo de Bash Shell de Google recomienda documentos "aquí" para "Si tiene que escribir cadenas de más de 80 caracteres". Ver la respuesta de @ tripleee .
Trevor Boyd Smith

google.github.io/styleguide/… - este es el enlace directo en el nuevo documento
jcollum

Respuestas:


161

Esto es lo que puedes desear

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

Si esto crea dos argumentos para hacer eco y solo desea uno, entonces veamos la concatenación de cadenas. En bash, colocando dos cadenas una al lado de la otra concatenadas:

$ echo "continuation""lines"
continuationlines

Entonces, una línea de continuación sin sangría es una forma de dividir una cadena:

$ echo "continuation"\
> "lines"
continuationlines

Pero cuando se usa una sangría:

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

Obtienes dos argumentos porque esto ya no es una concatenación.

Si desea una sola cadena que cruza líneas, al aplicar sangría pero no obtener todos esos espacios, un enfoque que puede intentar es deshacerse de la línea de continuación y usar variables:

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

Esto le permitirá tener un código con sangría limpia a expensas de variables adicionales. Si hace que las variables sean locales, no debería ser tan malo.


1
Gracias por su ayuda, pero si bien esto elimina los espacios, ahora son parámetros separados (Bash está interpretando los espacios en la segunda línea como un separador de parámetros) y ahora solo se imprimen correctamente debido al comando echo.

1
¡Oh, te gustaría una sola cadena (bash) para abarcar líneas! Ya lo veo.
Ray Toal

44
Solución con una variable:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman

30

Aquí los documentos con el <<-HEREterminador funcionan bien para cadenas de texto de varias líneas sangradas. Eliminará todas las pestañas iniciales del documento aquí. (Sin embargo, los terminadores de línea permanecerán).

cat <<-____HERE
    continuation
    lines
____HERE

Ver también http://ss64.com/bash/syntax-here.html

Si necesita preservar algunos, pero no todos, espacios en blanco, puede usar algo como

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

o tal vez lo use trpara deshacerse de las nuevas líneas:

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

(La segunda línea tiene una pestaña y un espacio al frente; el operador del tablero eliminará la pestaña antes del terminador heredoc, mientras que el espacio se conservará).

Para envolver cadenas largas y complejas en muchas líneas, me gusta printf:

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

También funciona bien en contextos donde desea incrustar piezas de script de shell no triviales en otro idioma donde la sintaxis del idioma del host no le permitirá usar un documento aquí, como en un Makefileo Dockerfile.

printf '%s\n' >./myscript \
    '#!/bin/sh` \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript

No funciona para mi Ubuntu 16.04. Obtengo dos líneas en lugar de una línea concatenada esperada.
Penghe Geng

@PengheGeng De hecho, esto resuelve el problema de deshacerse de la sangría, no el de unir líneas. Todavía puede volver la barra diagonal inversa al final de una línea para unir dos líneas.
tripleee

(Pero vea el primer printfejemplo también ahora.)
tripleee

12

Puedes usar matrices bash

$ str_array=("continuation"
             "lines")

luego

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

hay un espacio extra, porque (después del manual bash):

Si la palabra está entre comillas dobles, se ${name[*]}expande a una sola palabra con el valor de cada miembro de la matriz separado por el primer carácter de la variable IFS

Así que prepárate IFS=''para deshacerte del espacio extra

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

4

En ciertos escenarios, utilizar la capacidad de concatenación de Bash podría ser apropiado.

Ejemplo:

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

Desde la sección PARÁMETROS de la página Bash Man:

nombre = [valor] ...

... En el contexto donde una instrucción de asignación está asignando un valor a una variable de shell o índice de matriz, el operador + = se puede usar para agregar o agregar al valor anterior de la variable. Cuando + = se aplica a una variable para la cual se ha establecido el atributo entero, el valor se evalúa como una expresión aritmética y se agrega al valor actual de la variable, que también se evalúa. Cuando + = se aplica a una variable de matriz usando la asignación compuesta (ver Arreglos a continuación), el valor de la variable no se desarma (como lo es cuando se usa =), y los nuevos valores se agregan a la matriz comenzando en uno mayor que el índice máximo de la matriz (para matrices indexadas) o agregadas como pares clave-valor adicionales en una matriz asociativa. Cuando se aplica a una variable con valor de cadena, el valor se expande y se agrega al valor de la variable.


Probablemente el mejor consejo en esta página. El uso de un HEREDOC y la canalización en una traducción para <CR> s es muy poco intuitivo y falla cuando la cadena realmente necesita tener separadores de línea colocados discretamente.
ingyhere

2

Encontré una situación en la que tenía que enviar un mensaje largo como parte de un argumento de comando y tenía que cumplir con la limitación de longitud de línea. Los comandos se parecen a esto:

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

La forma en que resolví esto es mover el mensaje como un documento aquí (como sugirió @tripleee). Pero un documento aquí se convierte en un stdin, por lo que debe leerse nuevamente, seguí el siguiente enfoque:

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

Esto tiene la ventaja de que $messagepuede usarse exactamente como la constante de cadena sin espacios en blanco o saltos de línea adicionales.

Tenga en cuenta que las líneas de mensaje reales anteriores tienen el prefijo de un tabcarácter cada una, que se elimina aquí mismo documento (debido al uso de <<-). Todavía hay saltos de línea al final, que luego se reemplazan por ddespacios.

Tenga en cuenta también que si no elimina las nuevas líneas, aparecerán tal como están cuando "$message"se expanden. En algunos casos, es posible que pueda solucionarlo eliminando las comillas dobles $message, pero el mensaje ya no será un argumento único.


2

Las continuaciones de línea también se pueden lograr mediante el uso inteligente de la sintaxis.

En el 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.

En el 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.

Otro enfoque en el 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!


1

Simplemente puede separarlo con nuevas líneas (sin usar la barra invertida) según se requiera dentro de la sangría de la siguiente manera y solo una tira de nuevas líneas.

Ejemplo:

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

O si se trata de una definición variable, las nuevas líneas se convierten automáticamente en espacios. Por lo tanto, tira de espacios adicionales solo si corresponde.

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

1

Esto no es exactamente lo que pidió el usuario, pero otra forma de crear una cadena larga que abarca varias líneas es acumulándola gradualmente, de esta manera:

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

Obviamente en este caso hubiera sido más sencillo construirlo de una vez, pero este estilo puede ser muy ligero y comprensible cuando se trata de cuerdas más largas.


0

Sin embargo, si tiene un código sangrado, no funciona tan bien:

    echo "continuation \
    lines"
>continuation     lines

Pruebe con comillas simples y concatenando las cadenas:

    echo 'continuation' \
    'lines'
>continuation lines

Nota: la concatenación incluye un espacio en blanco.


2
Funciona con eco y argumentos de cadena, pero no funciona con otras cosas, como la asignación de variables. Aunque la pregunta no era sobre variables, el uso de echo fue solo un ejemplo. En lugar de echo si tenía x=, se obtendría el error: lines: command not found.
LS

0

Dependiendo de qué tipo de riesgos aceptará y qué tan bien conoce y confía en los datos, puede utilizar la interpolación de variables simplista.

$: 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

-2

Probablemente, esto realmente no responda a su pregunta, pero de todos modos puede resultarle útil.

El primer comando crea el script que muestra el segundo comando.

El tercer comando hace que ese script sea ejecutable.

El cuarto comando proporciona un ejemplo 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

Tenga en cuenta que si realmente desea usar este script, tiene más sentido mover el script ejecutable ~/binpara que esté en su camino.

Consulte la referencia de Python para obtener detalles sobre cómo textwrap.dedentfunciona.

Si el uso de $'...'o "$(...)"es confuso para usted, haga otra pregunta (una por construcción) si todavía no hay una. Puede ser bueno proporcionar un enlace a la pregunta que encuentre / haga para que otras personas tengan una referencia vinculada.


66
Bien intencionado, y posiblemente incluso útil, aunque esto puede ser, el OP le pidió consejo sobre la sintaxis bash básica, y usted le dio una definición de función de Python que utiliza paradigmas OO, control de flujo excepcional e importaciones. Además, llamó a un ejecutable como parte de una interpolación de cadenas, algo que una persona que hace este tipo de preguntas definitivamente no habría visto todavía en bash.
Parthian Shot
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.