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?
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
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
).
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 terminalif [ -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), sortiestdin
à la commande (cat -
etcat
sont abrégés pourcat /dev/stdin
)else cat - | ${COMMAND}
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.
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.