Lignes de suite Bash

158

Comment utilisez-vous les lignes de continuation bash?

Je réalise que vous pouvez faire ceci:

echo "continuation \
lines"
>continuation lines

Cependant, si vous avez du code en retrait, cela ne fonctionne pas si bien:

    echo "continuation \
    lines"
>continuation     lines
PyRulez
la source
5
Le guide de style Bash Shell de Google recommande des documents «ici» pour «Si vous devez écrire des chaînes de plus de 80 caractères». Voir la réponse de @ tripleee .
Trevor Boyd Smith
google.github.io/styleguide/… - c'est le lien direct dans le nouveau document
jcollum

Réponses:

161

C'est ce que tu veux peut-être

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

Si cela crée deux arguments à écho et que vous n'en voulez qu'un, alors regardons la concaténation de chaînes. Dans bash, placer deux chaînes côte à côte concaténent:

$ echo "continuation""lines"
continuationlines

Ainsi, une ligne de continuation sans retrait est un moyen de rompre une chaîne:

$ echo "continuation"\
> "lines"
continuationlines

Mais lorsqu'un retrait est utilisé:

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

Vous obtenez deux arguments car il ne s'agit plus d'une concaténation.

Si vous souhaitez une seule chaîne qui traverse les lignes, tout en indentant mais sans obtenir tous ces espaces, une approche que vous pouvez essayer est d'abandonner la ligne de continuation et d'utiliser des variables:

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

Cela vous permettra d'avoir un code clairement indenté au détriment de variables supplémentaires. Si vous rendez les variables locales, cela ne devrait pas être trop grave.

Ray Toal
la source
1
Merci pour votre aide, mais bien que cela supprime les espaces, ils sont maintenant des paramètres séparés (Bash interprète les espaces sur la deuxième ligne comme un séparateur de paramètres) et ne sont maintenant imprimés correctement qu'à cause de la commande echo.
1
Oh, vous voudriez qu'une seule chaîne (bash) couvre les lignes! Je vois maintenant.
Ray Toal
4
Solution à une variable:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman
30

Ici, les documents avec le <<-HEREterminateur fonctionnent bien pour les chaînes de texte sur plusieurs lignes indentées. Cela supprimera tous les onglets de début du document ici. (Les terminaisons de ligne resteront cependant toujours.)

cat <<-____HERE
    continuation
    lines
____HERE

Voir aussi http://ss64.com/bash/syntax-here.html

Si vous avez besoin de conserver certains espaces, mais pas tous, vous pouvez utiliser quelque chose comme

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

ou peut-être utiliser trpour se débarrasser des nouvelles lignes:

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

(La deuxième ligne a une tabulation et un espace à l'avant; la tabulation sera supprimée par l'opérateur de tiret avant le terminateur heredoc, tandis que l'espace sera conservé.)

Pour envelopper de longues chaînes complexes sur de nombreuses lignes, j'aime printf:

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

Cela fonctionne également bien dans les contextes où vous souhaitez incorporer des éléments non triviaux de script shell dans un autre langage où la syntaxe du langage hôte ne vous permettra pas d'utiliser un document here, comme dans un Makefileou Dockerfile.

printf '%s\n' >./myscript \
    '#!/bin/sh` \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript
tripleee
la source
Ça ne marche pas pour moi. Ubuntu 16.04. J'obtiens deux lignes au lieu d'une ligne concaténée attendue.
Penghe Geng
@PengheGeng En effet, cela résout le problème de la suppression de l'indentation, pas celui de la jonction des lignes. Vous pouvez toujours appliquer une barre oblique inverse à la nouvelle ligne à la fin d'une ligne pour joindre deux lignes ensemble.
tripleee
(Mais voyez aussi le premier printfexemple maintenant.)
tripleee
12

Vous pouvez utiliser des tableaux bash

$ str_array=("continuation"
             "lines")

puis

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

il y a un espace supplémentaire, car (après le manuel bash):

Si le mot est entre guillemets, se ${name[*]}développe en un seul mot avec la valeur de chaque membre du tableau séparée par le premier caractère de la variable IFS

Alors prêt IFS=''à se débarrasser de l'espace supplémentaire

$ IFS=''
$ echo "${str_array[*]}"
continuationlines
tworec
la source
4

Dans certains scénarios, l'utilisation de la capacité de concaténation de Bash peut être appropriée.

Exemple:

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

Dans la section PARAMÈTRES de la page Bash Man:

name = [valeur] ...

... Dans le contexte où une instruction d'affectation affecte une valeur à une variable shell ou à un index de tableau, l'opérateur + = peut être utilisé pour ajouter ou ajouter à la valeur précédente de la variable. Lorsque + = est appliqué à une variable pour laquelle l'attribut entier a été défini, la valeur est évaluée comme une expression arithmétique et ajoutée à la valeur actuelle de la variable, qui est également évaluée. Lorsque + = est appliqué à une variable de tableau à l'aide d'une affectation composée (voir Tableaux ci-dessous), la valeur de la variable n'est pas annulée (comme c'est le cas lors de l'utilisation de =) et de nouvelles valeurs sont ajoutées au tableau en commençant à un supérieur à l'index maximal du tableau (pour les tableaux indexés) ou ajoutés en tant que paires clé-valeur supplémentaires dans un tableau associatif. Lorsqu'elle est appliquée à une variable à valeur chaîne, la valeur est développée et ajoutée à la valeur de la variable.

Cybernaut
la source
Probablement le meilleur conseil sur cette page. Utiliser un HEREDOC et un piping dans une translation pour les <CR> s est super peu intuitif et échoue lorsque la chaîne a réellement besoin d'avoir des séparateurs de ligne placés discrètement.
ingyhere le
2

Je suis tombé sur une situation dans laquelle je devais envoyer un long message dans le cadre d'un argument de commande et je devais respecter la limite de longueur de ligne. Les commandes ressemblent à ceci:

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

La façon dont j'ai résolu ce problème est de déplacer le message en tant que document ici (comme @tripleee suggéré). Mais un document ici devient un stdin, il doit donc être relu, je suis allé avec l'approche ci-dessous:

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

Cela a l'avantage de $messagepouvoir être utilisé exactement comme constante de chaîne sans espace supplémentaire ni saut de ligne.

Notez que les lignes de message ci-dessus sont précédées d'un tabcaractère chacune, qui est supprimé par ici le document lui-même (en raison de l'utilisation de <<-). Il y a encore des sauts de ligne à la fin, qui sont ensuite remplacés par dddes espaces.

Notez également que si vous ne supprimez pas les nouvelles lignes, elles apparaîtront telles quelles lors du "$message"développement. Dans certains cas, vous pourrez peut-être contourner le problème en supprimant les guillemets $message, mais le message ne sera plus un argument unique.

haridsv
la source
2

Les continuations de ligne peuvent également être obtenues grâce à une utilisation intelligente de la syntaxe.

Dans le cas 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.

Dans le cas des 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.

Une autre approche dans le cas des 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!

ingyhere
la source
1

Vous pouvez simplement le séparer par des retours à la ligne (sans utiliser de barre oblique inverse) comme requis dans l'indentation comme suit et simplement supprimer les nouvelles lignes.

Exemple:

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

Ou s'il s'agit d'une définition de variable, les nouvelles lignes sont automatiquement converties en espaces. Donc, bande d'espaces supplémentaires uniquement le cas échéant.

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
la source
1

Ce n'est pas exactement ce que l'utilisateur a demandé, mais une autre façon de créer une longue chaîne qui s'étend sur plusieurs lignes est de la construire progressivement, comme ceci:

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

Évidemment, dans ce cas, il aurait été plus simple de le construire en une seule fois, mais ce style peut être très léger et compréhensible lorsqu'il s'agit de cordes plus longues.

Jon McClung
la source
0

Cependant, si vous avez du code en retrait, cela ne fonctionne pas si bien:

    echo "continuation \
    lines"
>continuation     lines

Essayez avec des guillemets simples et concaténant les chaînes:

    echo 'continuation' \
    'lines'
>continuation lines

Remarque: la concaténation comprend un espace.

mMontu
la source
2
Cela fonctionne avec des arguments d'écho et de chaîne, mais cela ne fonctionne pas avec d'autres choses, comme l'affectation de variables. Bien que la question ne concernait pas les variables, l'utilisation de l'écho n'était qu'un exemple. Au lieu de echo si vous aviez x=, vous obtiendrez l'erreur: lines: command not found.
LS
0

En fonction du type de risques que vous accepterez et de la qualité de votre connaissance et de votre confiance dans les données, vous pouvez utiliser une interpolation de variable simpliste.

$: 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
la source
-2

Cela ne répond probablement pas vraiment à votre question, mais vous pourriez le trouver utile de toute façon.

La première commande crée le script affiché par la deuxième commande.

La troisième commande rend ce script exécutable.

La quatrième commande fournit un exemple d'utilisation.

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

Notez que si vous voulez vraiment utiliser ce script, il est plus logique de déplacer le script exécutable ~/binpour qu'il soit dans votre chemin.

Consultez la référence python pour plus de détails sur son textwrap.dedentfonctionnement.

Si l'utilisation de $'...'ou "$(...)"est déroutante pour vous, posez une autre question (une par construction) s'il n'y en a pas déjà une. Il peut être intéressant de fournir un lien vers la question que vous trouvez / posez afin que d'autres personnes aient une référence liée.

intuité
la source
6
Bien intentionné - et peut-être même utile - si cela peut être, l'OP a demandé des conseils sur la syntaxe bash de base, et vous lui avez donné une définition de fonction python qui utilise des paradigmes OO, un contrôle de flux exceptionnel et des importations. De plus, vous avez appelé un exécutable dans le cadre d'une interpolation de chaîne - quelque chose qu'une personne posant ce genre de question n'aurait certainement pas encore vu dans bash.
Parthe Shot