web-dev-qa-db-fra.com

Chaîne multiligne avec espace supplémentaire (indentation préservée)

Je veux écrire des textes prédéfinis dans un fichier avec les éléments suivants:

text="this is line one\n
this is line two\n
this is line three"

echo -e $text > filename

J'attends quelque chose comme ça:

this is line one
this is line two
this is line three

Mais j'ai ceci:

this is line one
 this is line two
 this is line three

Je suis convaincu qu'il n'y a pas d'espace après chaque \n, mais comment l'espace supplémentaire est-il libéré?

270
cizixs

Heredoc semble plus pratique à cette fin. Il est utilisé pour envoyer plusieurs commandes à un programme interpréteur de commandes tel que ex ou cat .

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

La chaîne après «<<» indique où s’arrêter.

Pour envoyer ces lignes dans un fichier, utilisez:

cat > $FILE <<- EOM
Line 1.
Line 2.
EOM

Vous pouvez également stocker ces lignes dans une variable:

read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM

Ceci stocke les lignes dans la variable nommée VAR.

Lorsque vous imprimez, rappelez-vous les guillemets autour de la variable, sinon vous ne verrez pas les caractères de nouvelle ligne.

echo "$VAR"

Mieux encore, vous pouvez utiliser l'indentation pour le faire ressortir davantage dans votre code. Cette fois, ajoutez simplement un '-' après '<<' pour empêcher les onglets d'apparaître.

read -r -d '' VAR <<- EOM
    This is line 1.
    This is line 2.
    Line 3.
EOM

Mais ensuite, vous devez utiliser des tabulations, pas des espaces, pour l'indentation dans votre code.

463
new-kid

Si vous essayez d’obtenir la chaîne dans une variable, voici un autre moyen simple:

USAGE=$(cat <<-END
    This is line one.
    This is line two.
    This is line three.
END
)

Si vous indentez votre chaîne avec des tabulations (c'est-à-dire '\ t'), l'indentation sera supprimée. Si vous indentez avec des espaces, l'indentation sera laissée dans.

NOTE: est significatif que la dernière parenthèse fermante est sur une autre ligne. Le texte END doit apparaître seul sur une ligne.

104
Andrew Miner

echo ajoute des espaces entre les arguments qui lui sont transmis. $text est sujet à l'expansion des variables et au fractionnement de Word, votre commande echo est donc équivalente à:

echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n"  ...

Vous pouvez voir qu'un espace sera ajouté avant "ceci". Vous pouvez supprimer les caractères de nouvelle ligne et citer $text pour conserver les nouvelles lignes:

text="this is line one
this is line two
this is line three"

echo "$text" > filename

Vous pouvez aussi utiliser printf, qui est plus robuste et portable que echo:

printf "%s\n" "this is line one" "this is line two" "this is line three" > filename

Dans bash, qui prend en charge l'expansion du corset, vous pouvez même faire:

printf "%s\n" "this is line "{one,two,three} > filename
63
Josh Jolly

dans un script bash, les travaux suivants:

#!/bin/sh

text="this is line one\nthis is line two\nthis is line three"
echo $text > filename

alternativement:

text="this is line one
this is line two
this is line three"
echo "$text" > filename

cat nomfichier donne:

this is line one
this is line two
this is line three
32
Chris Maes

J'ai trouvé plus de solutions depuis que je voulais que chaque ligne soit correctement mise en retrait:

  1. Vous pouvez utiliser echo:

    echo    "this is line one"   \
        "\n""this is line two"   \
        "\n""this is line three" \
        > filename
    

    Cela ne fonctionne pas si vous mettez "\n" juste avant \ à la fin d'une ligne.

  2. Alternativement, vous pouvez utiliser printf pour une meilleure portabilité (j'ai eu beaucoup de problèmes avec echo):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
    
  3. Une autre solution pourrait être:

    text=''
    text="${text}this is line one\n"
    text="${text}this is line two\n"
    text="${text}this is line three\n"
    printf "%b" "$text" > filename
    

    ou 

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
    
  4. Une autre solution est obtenue en mélangeant printf et sed

    if something
    then
        printf '%s' '
        this is line one
        this is line two
        this is line three
        ' | sed '1d;$d;s/^    //g'
    fi
    

    Il n’est pas facile de refactoriser un code de la sorte, car vous codez en dur le niveau d’indentation dans le code.

  5. Il est possible d'utiliser une fonction d'assistance et quelques astuces de substitution de variable:

    unset text
    _() { text="${text}${text+
    }${*}"; }
    # That's an empty line which demonstrates the reasoning behind 
    # the usage of "+" instead of ":+" in the variable substitution 
    # above.
    _ ""
    _ "this is line one"
    _ "this is line two"
    _ "this is line three"
    unset -f _
    printf '%s' "$text"
    
24
Mateusz Piotrowski

cela fonctionnera si vous le mettez comme suit:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
1
luk

Ce qui suit est ma méthode préférée pour assigner une chaîne multiligne à une variable (je pense que ça a l'air sympa).

read -r -d '' my_variable << \
_______________________________________________________________________________

String1
String2
String3
...
StringN
_______________________________________________________________________________

Le nombre de traits de soulignement est le même (ici 80) dans les deux cas.

0