Reputation: 3974
I have some set of bash functions which output some information:
I've been writing functions which read output and filter it:
function filter-epson {
find-modelname-in-epson-ppds | sed <bla-blah-blah>
}
function filter-hp {
find-modelname-in-hp-ppds | sed <the same bla-blah-blah>
}
etc ...
But the I thought that it would be better do something like this:
function filter-general {
(somehow get input) | sed <bla-blah-blah>
}
and then call in another high-level functions:
function high-level-func {
# outputs filtered information
find-modelname-in-hp/epson/...-ppds | filter-general
}
How can I achieve that with the best bash practices?
Upvotes: 71
Views: 54561
Reputation: 189937
If you are asking how to avoid repeating the same sed
snippet in each of your functions, a simple solution is to put that in a function.
blah_blah () {
sed blah blah
}
filter-epson () {
find-modelname-in-epson-ppds | blah_blah
}
filter-hp () {
find-modelname-in-hp-ppds | blah_blah
}
In some situations, you might want to create a wrapper which postprocesses the output from whatever commands you pass in:
wrapper () {
"$@" | sed blah blah
}
filter-epson () {
wrapper find-modelname-in-epson-ppds
}
filter-hp () {
wrapper find-modelname-in-hp-ppds --some options --perchance
}
Tangentially notice also the preference for POSIX function syntax over the Bash-only function
keyword, which really adds no value here.
Upvotes: 0
Reputation: 328
The first command that takes STDIN but no command is piped to it, receives the STDIN from the function/script itself, but it happens only once and the next command without pipe, receives empty STDIN:
function stdTest {
echo 1:; echo "Local STDIN" | cat -n;
echo 2:; cat -n;
echo 3:; cat -n;
}
$ echo -e "Hey\nYou" | stdTest
1:
1 Local STDIN
2:
1 Hey
2 You
3:
Upvotes: 1
Reputation: 11114
A very simple means to get stdin into a variable is to use read
. By default, it reads file descriptor "0", i.e. stdin i.e., /dev/stdin
.
Example Function:
input(){ local in; read in; echo you said $in; }
Example implementation:
echo "Hello World" | input
Result:
you said Hello World
You don't need to declare a variable as being local, of course. I just included that for the sake of good form. Plain old read in
does what you need.
So you understand how read
works, by default it reads data off the given file descriptor (or implicit stdin) and blocks until it encounters a newline. Much of the time, you'll find that will implicitly be attached to your input, even if you weren't aware of it. If you have a function that seems to hang with this mechanism just keep this detail in mind (there are other ways of using read
to deal with that...).
Adding on to the basic example, here's a variation that lets you pass the input via a stdin OR an argument:
input()
{
local in=$1; if [ -z "$in" ]; then read in; fi
echo you said $in
}
With that tweak, you could ALSO call the function like:
input "Hello World"
How about handling an stdin option plus other arguments? Many standard nix utilities, especially those which typically work with stdin/stdout adhere to the common practice of treating a dash -
to mean "default", which contextually means either stdin or stdout, so you can follow the convention, and treat an argument specified as -
to mean "stdin":
input()
{
local a=$1; if [ "$a" == "-" ]; then read a; fi
local b=$2
echo you said $a $b
}
Call this like:
input "Hello" "World"
or
echo "Hello" | input - "World"
Going even further, there is actually no reason to only limit stdin to being an option for only the first argument! You might create a super flexible function that could use it for any of them...
input()
{
local a=$1; if [ "$a" == "-" ]; then read a; fi
local b=$2; if [ "$b" == "-" ]; then read b; fi
echo you said $a $b
}
Why would you want that? Because you could formulate, and pipe in, whatever argument you might need...
myFunc | input "Hello" -
In this case, I pipe in the 2nd argument using the results of myFunc
rather than the only having the option for the first.
Upvotes: 36
Reputation: 2276
If the question is How do I pass stdin to a bash function?
, then the answer is:
Shellscript functions take stdin the ordinary way, as if they were commands or programs. :)
input.txt:
HELLO WORLD
HELLO BOB
NO MATCH
test.sh:
#!/bin/sh
myfunction() {
grep HELLO
}
cat input.txt | myfunction
Output:
hobbes@metalbaby:~/scratch$ ./test.sh
HELLO WORLD
HELLO BOB
Note that command line arguments are ALSO handled in the ordinary way, like this:
test2.sh:
#!/bin/sh
myfunction() {
grep "$1"
}
cat input.txt | myfunction BOB
Output:
hobbes@metalbaby:~/scratch/$ ./test2.sh
HELLO BOB
Upvotes: 91
Reputation: 247210
To be painfully explicit that I'm piping from stdin, I sometimes write
cat - | ...
Upvotes: 37
Reputation: 362157
Call sed
directly. That's it.
function filter-general {
sed <bla-blah-blah>
}
Upvotes: 8