web-dev-qa-db-fra.com

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

Le problème:

  1. J'ai besoin d'attribuer à une variable une valeur décemment longue.
  2. Toutes les lignes de mon script doivent être sous un certain nombre de colonnes.

Donc, j'essaie de l'assigner en utilisant plus d'une ligne.

C'est simple de se passer des 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'affecter avec élégance sans ces espaces?

66
JamesL

Ici, le problème est que vous entourez la variable de guillemets doubles (""). Retirez-le et les choses fonctionneront bien.

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

Production

 This displays with extra spaces.

Ici, le problème est que le double guillemet d'une variable préserve tous les caractères d'espace blanc. Cela peut être utilisé au cas où vous en auriez explicitement besoin.

Par exemple,

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

imprimera

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

Et sur la suppression des citations, 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 pris comme un argument "unique" et préservant ainsi les espaces supplémentaires. Mais dans le second cas, la commande echo reçoit le texte en 5 arguments.

La citation d'une variable sera également utile lors du passage d'arguments aux commandes.

Dans la commande ci-dessous, echo obtient uniquement un seul argument comme "Hello World"

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

Mais dans le cas ci-dessous, echo obtient deux arguments comme Hello et World

$ variable="Hello World"
$ echo $variable
35
Kannan Mohan

Les solutions données par esuoxu et Mickaël Bucas sont les plus courantes et les plus portables façons de le faire.

Voici quelques solutions bash (dont certaines devraient également fonctionner dans d'autres shells, comme zsh). Tout d'abord avec le += opérateur d'ajout (qui fonctionne de manière légèrement différente pour chacune d'une variable entière, une variable régulière et 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 retours à la ligne (ou d'autres espaces/échappements) dans le texte, utilisez $'' citant à la place:

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

Ensuite, en utilisant printf -v pour affecter 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, donc contrairement à la plupart des fonctions printf, la bash réutilise la chaîne de format jusqu'à ce qu'elle soit épuisée. Vous pouvez mettre un \n dans la chaîne de format, ou utilisez $ '', (ou les deux) pour gérer 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 aussi 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 que tout le contenu du texte en une seule 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 entiers sont implicitement triés, contrairement aux tableaux associatifs) Cela vous donne un peu plus de flexibilité car vous pouvez manipuler les lignes et même tranche et dés si nécessaire.

Enfin, en utilisant read ou readarray et un "ici-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  

Le formulaire ici-document de <<- signifie que tous les onglets durs principaux sont supprimés de l'entrée, vous devez donc utiliser des onglets pour mettre en retrait votre texte. Citations autour de "EOT" empêche les fonctionnalités d'extension de Shell, donc l'entrée est utilisée textuellement. Avec read, il utilise une entrée délimitée par NUL octets, de sorte qu'il lira le texte délimité par la nouvelle ligne en une seule fois. Avec readarray (alias mapfile, disponible depuis bash-4.0), il lit dans un tableau, et -t supprime les retours à la ligne sur chaque ligne.

31
mr.spuratic

Il existe une syntaxe hérédoc spéciale qui supprime les onglets 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 tabulations supprimées

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 tabulations, pas des espaces.

13
Mickaël Bucas

Voici comment je vous suggère de le faire, et je vais vous 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 d'autres solutions proposées semblent suggérer que vous pouvez en quelque sorte affecter le contenu d'une variable Shell en modifiant vos méthodes de développement. 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}"

PRODUCTION

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 une expansion divisée en champs, puis un rapport sur le nombre d'octets pour la variable source de l'expansion, puis une expansion délimitée par des guillemets et le même nombre d'octets. Alors que la sortie peut différer du contenu de la variable Shell $string ne change jamais du tout sauf lors de l'affectation.

De plus, si vous ne comprenez pas pourquoi c'est, vous risquez de rencontrer des surprises très désagréables plus tôt que tard. Essayons à nouveau, mais dans des conditions légèrement différentes.

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

Même $string - environnement différent.

PRODUCTION

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

Le fractionnement de champ se produit sur la base des délimiteurs de champ définis dans $IFS. Il existe deux types de délimiteurs - $IFS espaces blancs et $IFS rien d'autre. Par défaut $IFS se voit attribuer la valeur nouvelle ligne de l'onglet espace - qui sont les trois possibles $IFS valeurs d'espaces. Cependant, il est facilement modifiable, comme vous pouvez le voir ci-dessus, et peut avoir des effets drastiques sur les extensions divisées en champs.

$IFS les espaces blancs seront éliminés par séquence à un seul champ - et c'est pourquoi echoing une expansion contenant n'importe quelle séquence d'espaces lorsque $IFS contient un espace sera évalué à un seul espace - car echo concatène ses arguments sur les espaces. Mais toutes les valeurs non blanches ne seront pas éliminées de la même manière, et chaque délimiteur qui se produit obtient toujours un champ pour lui-même - comme on peut le voir dans l'expansion stuff 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}"    

PRODUCTION

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

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

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

PRODUCTION

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 étendra les globes des noms de fichiers s'il peut les faire correspondre. Cela se produit après l'expansion des paramètres et la division du champ dans son ordre d'analyse et donc toute chaîne non citée est vulnérable de cette façon. Vous pouvez désactiver ce comportement avec set -f si vous le souhaitez, mais tout Shell compatible POSIX sera toujours glob par défaut.

C'est le genre de choses auxquelles vous êtes confronté lorsque vous déposez des devis 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 $string est toujours ce qu'il é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}"

PRODUCTION

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

Je pense que c'est une manière beaucoup plus saine d'adapter la syntaxe de Shell à vos préférences d'indentation. Ce que je fais ci-dessus est d'assigner chaque chaîne individuelle à un paramètre positionnel - qui peut être référencé par un nombre comme $1 ou ${33} - puis en affectant leurs valeurs concaténées à $var en utilisant le paramètre spécial Shell $*.

Cette approche n'est pas à l'abri de $IFS, Toutefois. Pourtant, je considère sa relation avec $IFS un 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"

PRODUCTION

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 voir, $* concatène chaque argument dans "$@" sur le premier octet de $IFS. Donc, sauver sa valeur tout en $IFS est affecté différemment obtient différents délimiteurs de champ pour chaque valeur enregistrée. Ce que vous voyez ci-dessus est la valeur littérale de chaque variable, soit dit en passant. Si vous ne vouliez aucun délimiteur, vous feriez:

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

PRODUCTION

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67
5
mikeserv

Laissez le shell manger les sauts de ligne indésirables 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 c'est sûr que c'est une question de goût d'aimer ou de détester cette solution ...

5
user62916

Vous pouvez peut-être essayer cela.

          echo "Test" \
               "Test2" \
               "Test3"
4
esuoxu

Vous voudrez peut-être essayer:

echo $VAR | tr -s " "

ou

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

et vous pouvez également vérifier this out.

2
Vivian Maya

Utiliser une extension de substitution Bash

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

$ echo "${VAR//  /}"
This displays with extra spaces.
2
CodeGnome

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)

L'utilisation de set écrase $@, qui peut être enregistrée et utilisée plus tard comme suit:

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

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

Cette approche complète est une variante de l'excellente réponse de mikeserv.

1
rsanden

N'ayez pas peur des espaces blancs. Il suffit de les supprimer 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

Production:

$ ./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 cassez votre indentation de 4 espaces avec des tabulations.

0
c0xc