Reputation: 5735
I have some scripts that work with parameters, they work just fine but i would like them to be able to read from stdin, from a pipe for example, an example, suppose this is called read:
#!/bin/bash
function read()
{
echo $*
}
read $*
Now this works with read "foo" "bar"
, but I would like to use it as:
echo "foo" | read
How do I accomplish this?
Upvotes: 48
Views: 93328
Reputation: 417
Yet another version that:
# Prints a text in a decorated ballon
function balloon()
{
(test -p /dev/stdin && cat - || echo $@) figlet -t | cowsay -n -f eyes | toilet -t --gay -f term
}
Usage:
# Using with a pipe
$ fortune -s | balloon
# Passing text as parameter
balloon "$(fortune -s )"
Upvotes: 0
Reputation: 1100
Late to the party here. Building off of @andy
's answer, here's how I define my to_uppercase
function.
to_uppercase() {
local input="$([[ -p /dev/stdin ]] && cat - || echo "$@")"
[[ -n "$input" ]] && echo "$input" | tr '[:lower:]' '[:upper:]'
}
Usages:
$ to_uppercase
$ to_uppercase abc
ABC
$ echo abc | to_uppercase
ABC
$ to_uppercase <<< echo abc
ABC
Bash version info:
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Upvotes: 5
Reputation: 2246
I've discovered that this can be done in one line using test
and awk
...
test -p /dev/stdin && awk '{print}' /dev/stdin
The test -p
tests for input on a pipe, which accepts input via stdin. Only if input is present do we want to run the awk
since otherwise it will hang indefinitely waiting for input which will never come.
I've put this into a function to make it easy to use...
inputStdin () {
test -p /dev/stdin && awk '{print}' /dev/stdin && return 0
### accepts input if any but does not hang waiting for input
#
return 1
}
Usage...
_stdin="$(inputStdin)"
Another function uses awk without the test to wait for commandline input...
inputCli () {
local _input=""
local _prompt="$1"
#
[[ "$_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
}
Usage...
_userinput="$(inputCli "Prompt string: ")"
Note that the > /dev/tty
on the first printf
seems to be necessary to get the prompt to print when the function is called in a Command Substituion $(...)
.
This use of awk
allows the elimination of the quirky read
command for collecting input from keyboard or stdin.
Upvotes: 0
Reputation: 17771
To combine a number of other answers into what worked for me (this contrived example turns lowercase input to uppercase):
uppercase() {
local COMMAND='tr [:lower:] [:upper:]'
if [ -t 0 ]; then
if [ $# -gt 0 ]; then
echo "$*" | ${COMMAND}
fi
else
cat - | ${COMMAND}
fi
}
Some examples (the first has no input, and therefore no output):
:; uppercase
:; uppercase test
TEST
:; echo test | uppercase
TEST
:; uppercase <<< test
TEST
:; uppercase < <(echo test)
TEST
Step by step:
test if file descriptor 0 (/dev/stdin
) was opened by a terminal
if [ -t 0 ]; then
tests for CLI invocation arguments
if [ $# -gt 0 ]; then
echo all CLI arguments to command
echo "$*" | ${COMMAND}
else if stdin
is piped (i.e. not terminal input), output stdin
to command (cat -
and cat
are shorthand for cat /dev/stdin
)
else
cat - | ${COMMAND}
Upvotes: 16
Reputation: 166319
Here is example implementation of sprintf
function in bash which uses printf
and standard input:
sprintf() { local stdin; read -d '' -u 0 stdin; printf "$@" "$stdin"; }
Example usage:
$ echo bar | sprintf "foo %s"
foo bar
This would give you an idea how function can read from standard input.
Upvotes: 6
Reputation: 530823
It's a little tricky to write a function which can read standard input, but works properly when no standard input is given. If you simply try to read from standard input, it will block until it receives any, much like if you simply type cat
at the prompt.
In bash 4, you can work around this by using the -t
option to read
with an argument of 0. It succeeds if there is any input available, but does not consume any of it; otherwise, it fails.
Here's a simple function that works like cat
if it has anything from standard input, and echo
otherwise.
catecho () {
if read -t 0; then
cat
else
echo "$*"
fi
}
$ catecho command line arguments
command line arguments
$ echo "foo bar" | catecho
foo bar
This makes standard input take precedence over command-line arguments, i.e., echo foo | catecho bar
would output foo
. To make arguments take precedence over standard input (echo foo | catecho bar
outputs bar
), you can use the simpler function
catecho () {
if [ $# -eq 0 ]; then
cat
else
echo "$*"
fi
}
(which also has the advantage of working with any POSIX-compatible shell, not just certain versions of bash
).
Upvotes: 47
Reputation: 289495
You can use <<<
to get this behaviour. read <<< echo "text"
should make it.
Test with readly
(I prefer not using reserved words):
function readly()
{
echo $*
echo "this was a test"
}
$ readly <<< echo "hello"
hello
this was a test
With pipes, based on this answer to "Bash script, read values from stdin pipe":
$ echo "hello bye" | { read a; echo $a; echo "this was a test"; }
hello bye
this was a test
Upvotes: 40