Comment faire une fonction bash qui peut lire à partir d'une entrée standard?

j'ai quelques scripts qui fonctionnent avec des paramètres, ils fonctionnent très bien, mais je voudrais être capable de lire à partir de stdin, à partir d'un tuyau par exemple, un exemple, supposons que c'est la lecture:

#!/bin/bash
function read()
{
 echo $*
}

read $*

Maintenant cela fonctionne avec read "foo" "bar", mais je voudrais l'utiliser en tant que:

echo "foo" | read

Comment puis-je accomplir ceci?

39
demandé sur kenorb 2013-09-12 14:01:37

5 réponses

Vous pouvez utiliser <<< pour obtenir ce comportement. read <<< echo "text" devrait s'en sortir.

tester avec readly (je préfère ne pas utiliser des mots réservés):

function readly()
{
 echo $*
 echo "this was a test"
}

$ readly <<< echo "hello"
hello
this was a test

avec des pipes, basé sur cette réponse à "script Bash, lire des valeurs à partir de stdin pipe":

$ echo "hello bye" | { read a; echo $a;  echo "this was a test"; }
hello bye
this was a test
39
répondu fedorqui 2017-05-23 12:02:14

C'est un peu compliqué d'écrire une fonction qui peut lire l'entrée standard, mais fonctionne correctement lorsque aucune norme d'entrée. Si vous essayez simplement de lire l'entrée standard, il permet de bloquer jusqu'à ce qu'il reçoit tout, un peu comme si vous tapez simplement cat à l'invite de commandes.

dans bash 4, Vous pouvez contourner cela en utilisant le -t option read avec un argument de 0. Il réussit s'il y a un input disponible, mais il ne consomme rien de celui-ci; autrement, il échouer.

Voici une fonction simple qui fonctionne comme cat s'il a quelque chose à partir de l'entrée standard, et echo autrement.

catecho () {
    if read -t 0; then
        cat
    else
        echo "$*"
    fi
}

$ catecho command line arguments
command line arguments
$ echo "foo bar" | catecho
foo bar

ceci fait que l'entrée standard a priorité sur les arguments en ligne de commande, i.e.,echo foo | catecho bar sortie foo. Pour faire les arguments ont priorité sur l'entrée standard (echo foo | catecho bar sorties bar), vous pouvez utiliser la fonction plus simple

catecho () {
    if [ $# -eq 0 ]; then
        cat
    else
        echo "$*"
    fi
}

(qui a aussi l'avantage de travailler avec Shell compatible POSIX, pas seulement certaines versions de bash).

33
répondu chepner 2014-09-16 13:09:40

Pour combiner un certain nombre d'autres réponses en ce qui a fonctionné pour moi (cet exemple artificiel se transforme en minuscules entrée en majuscules):

  uppercase() {
    local COMMAND='tr [:lower:] [:upper:]'
    if [ -t 0 ]; then
      if [ $# -gt 0 ]; then
        echo "$*" | ${COMMAND}
      fi
    else
      cat - | ${COMMAND} 
    fi
  }

Quelques exemples (le premier n'a pas d'entrée, et donc pas de sortie):

:; uppercase
:; uppercase test
TEST
:; echo test | uppercase 
TEST
:; uppercase <<< test
TEST
:; uppercase < <(echo test)
TEST

Étape par étape:

  • tester si le descripteur de fichier 0 (/dev/stdin) a été ouvert par un terminal

    if [ -t 0 ]; then
    
  • tests pour l'invocation de CLI les arguments

    if [ $# -gt 0 ]; then
    
  • echo toutes les CLI arguments à la commande

    echo "$*" | ${COMMAND}
    
  • sinon si stdin qui est joué (c'est à dire pas d'entrée de la borne), sortie stdin à la commande (cat - et cat sont abrégés pourcat /dev/stdin)

    else
      cat - | ${COMMAND}
    
13
répondu Andy 2016-05-29 15:26:39

voici un exemple de mise en œuvre de sprintf fonction en bash qui utilise printf et l'entrée standard:

sprintf() { local stdin; read -d '' -u 0 stdin; printf "$@" "$stdin"; }

Exemple d'utilisation:

$ echo bar | sprintf "foo %s"
foo bar

cela vous donnerait une idée de la façon dont la fonction peut lire à partir d'une entrée standard.

5
répondu kenorb 2015-05-29 17:02:25

j'ai découvert que cela peut être fait en une ligne en utilisant test et awk...

    test -p /dev/stdin  && awk '{print}' /dev/stdin

test -p essais d'entrée sur un tuyau, qui accepte l'entrée via stdin. Seulement si l'entrée est présent, nous ne voulons exécuter l' awk car sinon il sera suspendu indéfiniment en attente d'une entrée qui ne viendra jamais.

je l'ai mis dans une fonction pour le rendre facile à utiliser...

inputStdin () {
  test -p /dev/stdin  && awk '{print}' /dev/stdin  && return 0
  ### accepts input if any but does not hang waiting for input
  #
  return 1
}

Utilisation...

_stdin="$(inputStdin)"

une Autre fonction utilise awk sans le test pour attendre l'entrée en ligne de commande...

inputCli () {
  local _input=""
  local _prompt=""
  #
  [[ "$_prompt" ]]  && { printf "%s" "$_prompt" > /dev/tty; }
  ### no prompt at all if none supplied
  #
  _input="$(awk 'BEGIN {getline INPUT < "/dev/tty"; print INPUT}')"
  ### accept input (used in place of 'read')
  ###   put in a BEGIN section so will only accept 1 line and exit on ENTER
  ###   WAITS INDEFINITELY FOR INPUT
  #
  [[ "$_input" ]]  && { printf "%s" "$_input"; return 0; }
  #
  return 1
}

Utilisation...

_userinput="$(inputCli "Prompt string: ")"

notez que le > /dev/tty le premier printf semble être nécessaire pour obtenir l'invite à imprimer lorsque la fonction est appelée dans une Commande de Substitution $(...).

awk permet d'éliminer les excentricités read commande pour la collecte des entrées du clavier ou du stdin.

0
répondu DocSalvager 2018-05-11 19:04:33