Comment puis-je faire en sorte que l'opérateur bash backtick garde les nouvelles lignes en sortie?

36

Y at-il un moyen de faire bash ne pas manger de nouvelles lignes dans la substitution de backtick?

Par exemple:

var=`echo line one && echo line two`
echo $var

line one line two

Ce que je veux c'est

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two
poison lent
la source

Réponses:

54

Ce n'est pas un problème avec la substitution des backticks, mais avec echo; vous devez citer la variable pour que les caractères de contrôle fonctionnent:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two
cYrus
la source
Ainsi, bash supprime les retours à la ligne tout en développant une variable sur la commande-lien? Et ce comportement peut être désactivé en citant la variable?
slowpoison
7
basheffectue l' expansion des paramètres avant d' exécuter une commande, citant est nécessaire d'informer bashque vous souhaitez imprimer une seule chaîne et non 4 mots ( line, one, lineet two). Essayez: echo a <many spaces> bet echo "a <many spaces> b". Jetez un oeil à cette réponse aussi.
cYrus
5

Même si @cyrus est correct, cela ne répond pas réellement à toute la question et il n’explique aucune explication de ce qui se passe.

Alors laisse marcher à travers elle.

Newlines dans la chaîne

Déterminez d’abord la séquence d’octets attendue:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Maintenant, utilisez Commande de substitution (Section 3.5.4) pour tenter de capturer cette séquence d'octets:

$ x=$( echo a; echo b; )

Ensuite, faites un simple écho pour vérifier la séquence d'octets:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Il semble donc que la première nouvelle ligne a été remplacée par un espace et que la deuxième nouvelle ligne est restée intacte. Mais pourquoi ?

Regardons ce qui se passe réellement ici:

Premièrement, bash fera l’ extension des paramètres de shell (Section 3.5.3)

Le caractère '$' introduit le développement de paramètres, la substitution de commandes ou le développement arithmétique. Le nom du paramètre ou le symbole à développer peut être placé entre accolades, qui sont facultatifs mais servent à protéger la variable à développer des caractères qui le suivent immédiatement, qui pourraient être interprétés comme faisant partie du nom.

Ensuite, bash fera le fractionnement de mots (Section 3.5.7)

Le shell analyse les résultats du développement des paramètres, de la substitution de commande et du développement arithmétique qui ne se sont pas produits entre guillemets doubles pour le fractionnement des mots.

Le shell traite chaque caractère de $ IFS comme un séparateur et divise les résultats des autres extensions en mots sur ces caractères. Si IFS n'est pas défini, ou si sa valeur est exactement, ...

Ensuite, bash le traitera comme une commande simple (Section 3.2.1)

Une commande simple est le type de commande le plus souvent rencontré. C'est juste une séquence de mots séparés par des blancs, terminés par l'un des opérateurs de contrôle du shell (voir Définitions). Le premier mot spécifie généralement une commande à exécuter, le reste des mots étant les arguments de cette commande.

La définition des blancs (Section 2 - Définitions)

blank Espace ou tabulation.

Enfin, bash appelle la commande interne echo (Section 4.2 - Commandes intégrées de Bash)

... Affiche les arguments, séparés par des espaces, terminés par une nouvelle ligne. ...

Donc, pour résumer, les nouvelles lignes sont supprimées par Word Splitting, puis echo obtient 2 arguments, "a" et "b", puis les génère séparés par des espaces et se terminant par une nouvelle ligne.

En faisant ce que @cyrus a suggéré (et supprimez la nouvelle ligne de echo avec -n), le résultat est meilleur:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Newlines à la fin de la chaîne

Ce n'est toujours pas parfait, la fin de ligne est partie. Regarder de plus près la substitution de commande (Section 3.5.4) :

Bash exécute le développement en exécutant commande et en remplaçant la substitution de commande par la sortie standard de la commande, avec tout retour à la ligne final supprimé.

Maintenant que la raison pour laquelle la nouvelle ligne disparaît est claire, bash peut être trompé pour la conserver. Pour ce faire, ajoutez une chaîne supplémentaire à la fin et supprimez-la lorsque vous utilisez la variable:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Ajoutez une partie supplémentaire à la fin de la sortie et des variables de citation:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46
Iwan Aucamp
la source
0

J'aurais mis mon petit ajout dans un commentaire sur la réponse de cYrus, mais je ne peux pas encore commenter les réponses que je pense. Je vais donc dupliquer une partie de sa réponse pour la complétude.

Ce qui se passe dans la première ligne de votre code, c’est qu’en fait, la substitution des backticks mange les retours à la ligne de fin de la sortie de la commande substituée, comme documenté . Mais il ne mange pas la nouvelle ligne entre vos deux lignes.

Ce qui se passe dans la deuxième ligne de code, c’est que l’écho est exécuté avec deux arguments, le premier et le second. Citer la variable développée résoudra le problème suivant: echo "$var"Ceci préservera la nouvelle ligne entre votre ligne un et votre ligne deux. Et echoajoutera un retour à la ligne par défaut pour que tout se passe bien.

Maintenant, si vous souhaitez conserver tous les retours à la ligne dans la sortie d'une substitution de commande, une solution consiste à ajouter une autre ligne à la sortie, que vous pouvez supprimer après la substitution de commande, voir ici .

Gaston
la source