Comment affecter une valeur de chaîne à une variable sur plusieurs lignes en retrait?

54

Le problème:

  1. Je dois attribuer à une variable une valeur assez longue.
  2. Toutes les lignes de mon script doivent être sous un certain nombre de colonnes.

Donc, j'essaye de l'attribuer en utilisant plus d'une ligne.

C'est simple à faire sans retraits:

VAR="This displays without \
any issues."
echo "${VAR}"

Résultat:

This displays without any issues.

Cependant avec des tirets:

    VAR="This displays with \
    extra spaces."
    echo "${VAR}"

Résultat:

This displays with      extra spaces.

Comment puis-je l'assigner avec élégance sans ces espaces?

Sman865
la source

Réponses:

31

Ici, le problème est que vous entourez la variable avec des guillemets doubles (""). Supprimez-le et tout ira bien.

    VAR="This displays with \
    extra spaces."
    echo ${VAR}

Sortie

 This displays with extra spaces.

Ici, le problème est que les doubles guillemets d'une variable préservent tous les caractères d'espaces. Ceci peut être utilisé dans le cas où vous en avez explicitement besoin.

Par exemple,

$ echo "Hello     World    ........ ...            ...."

imprimera

Hello     World    ........ ...            ....

Et en enlevant les guillemets, ses différents

$ echo Hello     World    ........ ...            ....
Hello World ........ ... ....

Ici, le Bash supprime les espaces supplémentaires dans le texte car, dans le premier cas, le texte entier est considéré comme un argument "unique", préservant ainsi les espaces supplémentaires. Mais dans le second cas, la echocommande reçoit le texte sous forme de 5 arguments.

Il est également utile de citer une variable lors de la transmission des arguments aux commandes.

Dans la commande ci-dessous, echon’obtient qu’un seul argument en tant que"Hello World"

$ variable="Hello World"
$ echo "$variable"

Mais dans le cas du scénario ci-dessous, on echoobtient deux arguments au fur Helloet à mesure queWorld

$ variable="Hello World"
$ echo $variable
Kannan Mohan
la source
La plupart des réponses ont bien fonctionné, mais c'était la plus simple. Merci!
Sman865
12
@ Sman865 - S'il vous plaît, croyez-moi quand je vous dis que c'est en fait la réponse la plus compliquée proposée ici, et qu'elle est également fausse à presque tous les égards - en particulier dans sa déclaration d'ouverture. Toute question relative à la valorisation ne peut en aucun cas être liée à son expansion ultérieure - cela n’est qu’une régression. Je suis désolé Kannan, mais cette réponse est à la fois fausse et erronée. Les extensions de division de champs $IFSsont un outil puissant et universel - mais le code écrit de cette manière ne peut jamais produire de résultats fiables, de quelque type que ce soit.
mikeserv
2
Ne pas utiliser les guillemets lors du développement d'une variable pose tôt ou tard des problèmes, à commencer par le développement du nom de fichier (any of * ? []).
mr.spuratic
Je conviens que les variables doivent être entourées de guillemets doubles pour l’empêcher d’expansion bash. Mais dans des cas particuliers, nous pouvons l'éviter pour éviter la complication du code.
Kannan Mohan
1
Je suis d'accord avec @mikeserv, c'est un très mauvais conseil si on le prend à la valeur nominale. Cela cassera si utilisé sans les conséquences. Par exemple, si la variable est passée en tant qu'argument à une commande, vous ne voulez pas que chaque mot soit divisé en un argument séparé.
haridsv
28

Les solutions proposées par esuoxu et Mickaël Bucas sont le moyen le plus courant et le plus portable de le faire.

Voici quelques bashsolutions (dont certaines devraient également fonctionner dans d'autres coques, comme zsh). Tout d'abord avec l' +=opérateur append (qui fonctionne de manière légèrement différente pour chacune d'une variable entière, d'une variable régulière et d'un tableau).

text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
text+="tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
text+="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." 

Si vous voulez des nouvelles lignes (ou d'autres espaces / espaces) dans le texte, utilisez $''plutôt la citation:

text=$'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n'
text+=$'...'

Ensuite, en utilisant printf -vpour assigner une valeur formatée à une variable

printf -v text "%s" "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " \
                    "do eiusmod empor incididunt ut labore et dolore magna aliqua. "\
                    "Ut enim ad minim veniam ..."

L'astuce ici est qu'il y a plus d'arguments que de spécificateurs de format. Ainsi, contrairement à la plupart des printffonctions, la personne bash réutilise la chaîne de formatage jusqu'à épuisement. Vous pouvez insérer une \nchaîne dans le format ou utiliser $ '' (ou les deux) pour traiter les espaces.

Ensuite, en utilisant un tableau:

text=("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
      "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
      "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." )

Vous pouvez également utiliser +=pour construire le texte ligne par ligne (notez le ()):

text+=("post script")

Ici cependant, vous devez vous rappeler d’aplatir le tableau si vous voulez tout le contenu du texte en une fois.

echo "$text"      # only outputs index [0], the first line
echo "${text[*]}" # output complete text (joined by first character of IFS)

(Les tableaux indexés sur des entiers sont implicitement triés, contrairement aux tableaux associatifs). Cela vous donne un peu plus de souplesse, car vous pouvez manipuler des lignes et même des tranches et des dés si nécessaire.

Enfin, en utilisant readou readarrayet un "here-document":

read -r -d '' text <<-"EOT"
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ...
EOT

readarray -t textarray <<-"EOT"
        Lorem [...]
EOT  

La forme ici-document de <<-signifie que tous les onglets durs principaux sont supprimés de l'entrée, vous devez donc utiliser des onglets pour indenter votre texte. Les guillemets "EOT"empêchent les fonctionnalités d'expansion du shell, donc l'entrée est utilisée telle quelle. Avec readcela utilise une entrée délimitée par des octets NUL, de sorte qu'elle lise le texte délimité par une nouvelle ligne en une fois. Avec readarray(alias mapfile, disponible depuis bash-4.0), il lit dans un tableau et -tsupprime les nouvelles lignes sur chaque ligne.

mr.spuratic
la source
1
L' readoption avec here documentest très sympa! Utile pour incorporer des scripts python et exécuter avec python -c. Je faisais script=$(cat <here document>)avant, mais read -r -d '' script <here document>c'est beaucoup mieux.
haridsv
9

Il existe une syntaxe heredoc spéciale qui supprime les tabulations au début de toutes les lignes: "<< -" (remarquez le tiret ajouté)

http://tldp.org/LDP/abs/html/here-docs.html

Exemple 19-4. Message multiligne, avec onglets supprimés

Vous pouvez l'utiliser comme ceci:

v="$(cat <<-EOF
    A
        B
    C
EOF
)"
echo "$v"

Résultat :

A
B
C

Cela ne fonctionne qu'avec des onglets, pas d'espaces.

Mickaël Bucas
la source
Ici sur ubuntu, l'inverse est vrai - les espaces fonctionnent, pas les onglets. \tfonctionne très bien si vous avez besoin d'onglets. Il suffit d'utiliser echo -e "$v"plutôt que echo "$v"d'activer les caractères antislash
will-ob le
5

Laissez la coquille dévorer les sauts de ligne et les espaces suivants:

$ cat weird.sh 
#!/bin/sh

        var1="A weird(?) $(
             )multi line $(
             )text idea. $(
             )PID=$$"

        var2='You can '$(
            )'avoid expansion '$(
            )'too: PID=$$'

        var3='Or mix it: '$(
            )'To insert the PID use $$. '$(
            )"It expands to e.g. $$."

        echo "$var1"
        echo "$var2"
        echo "$var3"
$ sh weird.sh 
A weird(?) multi line text idea. PID=13960
You can avoid expansion too: PID=$$
Or mix it: To insert the PID use $$. It expands to e.g. 13960.

Donc c'est possible ... mais bien sûr, c'est une question de goût d'aimer ou de ne pas aimer cette solution ...

Yéti
la source
3
chacun d'entre eux est une fourchette.
mikeserv
5

Peut-être que vous pouvez essayer ceci.

          echo "Test" \
               "Test2" \
               "Test3"
esuoxu
la source
5

Voici comment je vous suggère de le faire, et je vais expliquer pourquoi, mais je veux d'abord parler d'autre chose ...

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"

Beaucoup des autres solutions proposées ici semblent suggérer que vous pouvez modifier le contenu d'une variable shell en modifiant vos méthodes d'extension. Je peux vous assurer que ce n'est pas le cas.

    string="some stuff here \
            some more stuff here."
    echo $string ${#string} 
    echo "$string" "${#string}"

SORTIE

some stuff here some more stuff here. 53
some stuff here                 some more stuff here. 53

Ce que vous voyez ci-dessus est d'abord un développement par division de champ, puis un rapport sur le nombre d'octets pour la variable source du développement, puis un développement délimité par des guillemets et le même nombre d'octets. Bien que la sortie puisse différer, le contenu de la variable shell $stringne change jamais, sauf lors de l'affectation.

De plus, si vous ne comprenez pas pourquoi, il est certain que vous rencontrerez de très mauvaises surprises le plus tôt possible. Essayons encore, mais dans des conditions légèrement différentes.

    IFS=sf
    echo $string ${#string} 
    echo "$string" "${#string}"

Même $string- environnement différent.

SORTIE

 ome  tu   here                  ome more  tu   here. 53
some stuff here                 some more stuff here. 53

La division de champ se produit en fonction des délimiteurs de champ définis dans $IFS. Il existe deux types de délimiteurs: les $IFSespaces et $IFStout le reste. Par défaut, $IFSl’ espace de valeur newline est affecté à l’ espace de valeur - qui sont les trois $IFSvaleurs d’espace possibles . Comme vous pouvez le constater ci-dessus, il est facile à modifier et peut avoir des effets considérables sur les développements en division de champ.

$IFSles espaces blancs vont élider par séquence en un seul champ - et c’est pourquoi echoun développement contenant une séquence d’espaces $IFScontenant un espace n’évaluera qu’un seul espace - car echoconcatène ses arguments sur des espaces. Mais toutes les valeurs non-blancs ne sera pas Elide de la même manière, et chaque delimiter se produit obtient toujours un champ à elle - même - comme on peut le voir dans la substance d' expansion ci - dessus.

Ce n'est pas le pire. Considérez cet autre $string.

IFS=$space$tab$newline
cd emptydir
    string=" * * * \
             * * * "
    echo $string ${#string}
    echo "$string" "${#string}"    

SORTIE

* * * * * * 30
 * * *                  * * *  30

Ça a l'air d'accord, non? Eh bien, modifions à nouveau l'environnement.

    touch file1 file2 file3 file4 file5
    echo $string ${#string}
    echo "$string" "${#string}"    

SORTIE

file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 30
 * * *                  * * *  30

Woah.

Par défaut, le shell développera les fichiers de noms de fichiers globs s'il peut leur correspondre. Cela se produit après le développement des paramètres et la division de champs dans son ordre d'analyse, de sorte que toute chaîne non mise entre guillemets est vulnérable de cette manière. Vous pouvez désactiver ce comportement set -fsi vous le souhaitez, mais tout shell compatible POSIX sera toujours glob par défaut.

C’est le genre de choses que vous rencontrez lorsque vous déposez des guillemets sur des extensions en fonction de vos préférences d’indentation. Et même ainsi, dans tous les cas, quel que soit son comportement d'expansion, la valeur réelle de $stringreste identique à ce qu'elle était lors de la dernière affectation. Revenons donc à la première chose.

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"
echo "$var" "${#var}"

SORTIE

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like. 70

Je pense que c'est un moyen beaucoup plus sain d'adapter la syntaxe du shell à vos préférences d'indentation. Ce que je fais ci-dessus consiste à affecter chaque chaîne individuelle à un paramètre de position - qui peut être référencé par un numéro comme $1ou ${33}-, puis à attribuer ses valeurs concaténées à l' $varaide du paramètre shell spécial $*.

Cette approche n’est cependant pas à l’abri $IFS. Néanmoins, je considère que sa relation avec $IFSun avantage supplémentaire à cet égard. Considérer:

IFS=\ ;space_split="$*"
IFS=/; slash_split="$*";IFS='
';new_line_split="$*"

echo "$space_split"
echo "$slash_split"
echo "$new_line_split"

SORTIE

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like.
Arg 1: Line 1./Arg 2: Line 2./and so on for/as long as you might like.
Arg 1: Line 1.
Arg 2: Line 2.
and so on for
as long as you might like.

Comme vous pouvez le constater, $*concatène chaque argument dans "$@"le premier octet $IFS. Ainsi, enregistrer sa valeur pendant qu'elle $IFSest affectée différemment donne différents délimiteurs de champs pour chaque valeur enregistrée. Ce que vous voyez ci-dessus est la valeur littérale de chaque variable, au fait. Si vous ne vouliez pas de délimiteur, vous feriez:

IFS=;delimitless="$*"
echo "$delimitless" "${#delimitless}"

SORTIE

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67
Mikeserv
la source
2

Vous voudrez peut-être essayer:

echo $VAR | tr -s " "

ou

myArr=($VAL)
VAL=${myArr[@]}
echo "$VAL"

et aussi vous pouvez vérifier cela .

Vivian Maya
la source
2

Utiliser une extension de substitution Bash

Si vous utilisez Bash, vous pouvez utiliser une extension de substitution . Par exemple:

$ echo "${VAR//  /}"
This displays with extra spaces.
CodeGnome
la source
1

Ceci est une variante pour définir des variables de type chemin:

set -- "${MYDIR}/usr/local/lib" \
      :"${MYDIR}/usr/lib" \
      :"${MYDIR}/lib" \
       "${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
    export LD_LIBRARY_PATH="$*"
    LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)

Utilisation des setécrasements $@, qui peuvent être enregistrés et utilisés ultérieurement comme suit:

ARGV=("$@")
exec foo "${ARGV[@]}"

La LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)ligne élimine les espaces avant les deux points ainsi que les éventuels espaces de fin. Si seulement éliminer les espaces de fin, utilisez LD_LIBRARY_PATH=${LD_LIBRARY_PATH%% }plutôt.

Toute cette approche est une variante de l'excellente réponse de mikeserv.

rsanden
la source
0

N'ayez pas peur des caractères d'espaces. Supprimez-les simplement avant d’imprimer votre texte multiligne.

$ cat ./t.sh
#!/bin/bash

NEED_HELP=1
if [[ $NEED_HELP -eq 1 ]]; then

    lucky_number=$((1 + RANDOM % 10 + 10))

    read -r -d '' text <<'    EOF'
    NAME says hello

    Look at this helpful text:

                                                 * -**
                                 Eyjafjallajokull        Eyjafjallajokull
                            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

    Don't go away, please rate your experience... It'll just take two minutes.

    EOF
    text=$(echo "$text" | sed -r 's!^\s{4}!!')
    text=$(echo "$text" | sed -r "s!\bNAME\b!$0!") # Bash: text=${text//NAME/$0}
    text=$(echo "$text" | sed -r "s!\btwo\b!$lucky_number!")

    echo "$text"

fi

Sortie:

$ ./t.sh
./t.sh says hello

Look at this helpful text:

                                             * -**
                             Eyjafjallajokull        Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

Don't go away, please rate your experience... It'll just take 16 minutes.

Pas besoin d'utiliser un <<-heredoc et de casser votre indentation de 4 espaces avec des caractères de tabulation.

c0xc
la source