87 votes

Comment éviter la substitution de la commande bash pour supprimer le caractère de nouvelle ligne ?

Pour accélérer l'exécution de certains script de bash, je voudrais conserver le résultat d'une commande dans une variable en utilisant la substitution de commande, mais la substitution de commande remplace le 0x0A le caractère de nouvelle ligne par un espace. Par exemple :

a=`df -H`

ou

a=$( df -H )

Quand je veux poursuivre le traitement $a les caractères de nouvelle ligne sont remplacés par un espace et toutes les lignes sont maintenant sur une seule ligne, ce qui est beaucoup plus difficile à greper :

echo $a

Quelles seraient les astuces simples pour éviter que le caractère de nouvelle ligne soit supprimé par la substitution de commande ?

138voto

user000001 Points 12050

Les nouvelles lignes non finales ne sont pas supprimées

Les retours à la ligne que vous recherchez sont là, mais vous ne les voyez pas, parce que vous utilisez la commande echo sans citer la variable.

Validation :

$ a=$( df -H )
$ echo $a
Filesystem Size Used Avail Use% Mounted on /dev/sda3 276G 50G 213G 19% / udev 2.1G 4.1k 2.1G 1% /dev tmpfs 832M 820k 832M 1% /run none 5.3M 0 5.3M 0% /run/lock none 2.1G 320k 2.1G 1% /run/shm
$ echo "$a"
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda3       276G   50G  213G  19% /
udev            2.1G  4.1k  2.1G   1% /dev
tmpfs           832M  820k  832M   1% /run
none            5.3M     0  5.3M   0% /run/lock
none            2.1G  320k  2.1G   1% /run/shm
$ 

Traînée les nouvelles lignes sont supprimées

Comme @user4815162342 correctement signalée, bien que les nouvelles lignes dans la sortie ne soient pas supprimées, nouvelles lignes de queue sont supprimés par substitution de commande. Voir l'expérience ci-dessous :

$ a=$'test\n\n'
$ echo "$a"
test

$ b=$(echo "$a")
$ echo "$b"
test
$

Dans la plupart des cas, cela n'a pas d'importance, car echo ajoutera le saut de ligne supprimé (à moins qu'il ne soit invoqué avec la directive -n ), mais il y a des cas limites où il y a plus d'un saut de ligne de fin dans la sortie d'un programme, et ils sont significatifs pour une raison quelconque.

Solutions de contournement

1. Ajouter un caractère fictif

Dans ce cas, comme @Scrutinizer mentionné, vous pouvez utiliser la solution de contournement suivante :

$ a=$(printf 'test\n\n'; printf x); a=${a%x}
$ echo "$a"
test

$ 

Explication : Caractère x est ajouté à la sortie (en utilisant printf x ), après les retours à la ligne. Comme les retours à la ligne ne sont pas suivi de plus, ils ne sont pas supprimés par la substitution de commande. L'étape suivante consiste à supprimer le x nous avons ajouté, en utilisant le % opérateur en ${a%x} . Maintenant nous avons la sortie originale, avec toutes les nouvelles lignes présentes ! !!

2. Lire en utilisant la substitution de processus

Au lieu d'utiliser la substitution de commande pour assigner la sortie d'un programme à une variable, nous pouvons à la place utiliser substitution de processus pour envoyer la sortie du programme au read commande intégrée (crédit à @ormaaj ). La substitution de processus préserve toutes les nouvelles lignes. Lire la sortie dans une variable est un peu délicat, mais vous pouvez le faire comme ceci :

$ IFS= read -rd '' var < <( printf 'test\n\n' ) 
$ echo "$var"
test

$ 

Explication :

  • Nous avons fixé le séparateur de champ interne pour la commande de lecture à null, avec IFS= . Sinon, read n'affecterait pas la totalité de la sortie à var mais seulement le premier jeton.
  • Nous invoquons read avec des options -rd '' . Le site r permet d'empêcher la barre oblique inversée d'agir comme un caractère spécial, et avec d '' définir le délimiteur à rien, afin que read lise la sortie entière, au lieu de seulement la première ligne.

3. Lire à partir d'un tuyau

Au lieu d'utiliser la substitution de commande ou de processus pour affecter la sortie d'un programme à une variable, nous pouvons à la place acheminer la sortie du programme vers la fonction read (crédit à @ormaaj ). Le piping préserve également toutes les nouvelles lignes. Notez cependant que, cette fois, nous définissons le paramètre lastpipe comportement optionnel de l'obus, en utilisant le site shopt intégré . Ceci est nécessaire, afin que le read est exécutée dans l'environnement actuel du shell. Sinon, la variable sera assignée dans un sous-shell, et elle ne sera pas accessible depuis le reste du script.

$ cat test.sh 
#!/bin/bash
shopt -s lastpipe
printf "test\n\n" | IFS= read -rd '' var
echo "$var"
$ ./test.sh 
test

$

1voto

ワイきんぐ Points 584

J'essayais de comprendre cela parce que j'utilisais bash pour transmettre le résultat de l'exécution de l'interpréteur sur un F# script. Après quelques essais et erreurs, cela s'est avéré résoudre le problème :

$ cat fsi.ch
#!/bin/bash
echo "$(fsharpi --quiet --exec --nologo $1)"

$ fsi.ch messages.fsx
Welcome to my program. Choose from the menu:
new | show | remove

En supposant, bien sûr, que vous ayez besoin d'exécuter un programme terminal. J'espère que cela vous aidera.

1voto

daphtdazz Points 3824

Une autre "astuce" consiste à utiliser le caractère retour chariot, qui empêche la suppression de la nouvelle ligne mais n'ajoute rien à la sortie :

$ my_func_1 () {
>     echo "This newline is squashed"
> }
$ my_func_2 () {
>     echo "This newline is not squashed"
>     echo -n $'\r'
> }
$ echo -n "$(my_func_1)" && echo -n "$(my_func_2)" && echo done
This newline is squashedThis newline is not squashed
done
$

Mais attention : comme mentionné dans les commentaires, cela peut fonctionner correctement pour une sortie qui va simplement dans le terminal, mais si vous transmettez cette sortie à un autre processus, vous risquez de le perturber car il ne s'attendra probablement pas à cette étrange terminaison. '\r' .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X