web-dev-qa-db-fra.com

Comment lire une entrée multiligne de stdin en variable et comment en imprimer une dans Shell (sh, bash)?

Ce que je veux faire est le suivant:

  1. lire sur plusieurs lignes l'entrée de stdin dans la variable A
  2. effectuer diverses opérations sur A
  3. pipe A sans perdre les symboles de délimitation (\n, \r, \t, etc.) vers une autre commande

Le problème actuel est que je ne peux pas le lire avec la commande read, car il arrête la lecture à la nouvelle ligne.

Je peux lire stdin avec cat, comme ceci:

my_var=`cat /dev/stdin`

, mais je ne sais pas comment l'imprimer. Pour que la nouvelle ligne, l'onglet et les autres délimiteurs soient toujours là.

Mon exemple de script ressemble à ceci:

#!/usr/local/bin/bash

A=`cat /dev/stdin`

if [ ${#A} -eq 0 ]; then
        exit 0
else
        cat ${A} | /usr/local/sbin/nextcommand
fi
55
kakas

Cela fonctionne pour moi:

myvar=`cat`

echo "$myvar"

Les citations autour de $myvar sont importants.

63
Tanktalus

Dans Bash, il existe un autre moyen; man bash Mentionne:

La substitution de commande $(cat file) peut être remplacée par l'équivalent mais plus rapide $(< file).

$ myVar=$(</dev/stdin)
hello
this is test
$ echo "$myVar"
hello
this is test
24
Ingo Karkat

tee fait le travail

#!/bin/bash
myVar=$(tee)
9
Sergey Grigoriev

Oui, ça marche aussi pour moi. Merci.

myvar=`cat`

est le même que

myvar=`cat /dev/stdin`

Hé bien oui. Depuis la page de manuel bash:

L'insertion de caractères entre guillemets conserve la valeur littérale de tous les caractères entre guillemets, à l'exception de $, `,\et, lorsque l'expansion de l'historique est activée,!. Les caractères $ et `conservent leur signification particulière entre guillemets doubles.

7
kakas

Si vous vous souciez de conserver les sauts de ligne à la fin de la sortie, utilisez ceci:

myVar=$(cat; echo x)
myVar=${myVar%x}
printf %s "$myVar"

Cela utilise l'astuce de ici .

4
Ingo Karkat

[mis à jour]

Cette affectation va se bloquer indéfiniment s'il n'y a rien dans le tuyau ...

var="$(< /dev/stdin)"

Nous pouvons cependant éviter cela en faisant un timeout read pour le premier caractère. S'il expire, le code retour sera supérieur à 128 et nous saurons que le canal STDIN (a.k.a /dev/stdin) Est vide.

Sinon, nous obtenons le reste de STDIN par ...

  • définissant IFS sur NULL uniquement pour la commande read
  • désactiver les échappements avec -r
  • éliminer le délimiteur de lecture avec -d ''.
  • et enfin, en ajoutant cela au personnage que nous avons obtenu initialement

Donc...

__=""
_stdin=""

read -N1 -t1 __  && {
  (( $? <= 128 ))  && {
    IFS= read -rd '' _stdin
    _stdin="$__$_stdin"
  }
}

Cette technique évite d'utiliser la substitution de commandes var="$(command ...)" qui, de par sa conception, supprimera toujours les nouvelles lignes de fin.

Si la substitution de commande est préférée, pour préserver les retours à la ligne de fin, nous pouvons ajouter un ou plusieurs caractères de délimitation à la sortie à l'intérieur de $(), puis les supprimer à l'extérieur.

Par exemple (notez $(parens) dans la première commande et ${braces} Dans la seconde) ...

_stdin="$(awk '{print}; END {print "|||"}' /dev/stdin)"
_stdin="${_stdin%|||}"
3
DocSalvager