I have a script outputting some value/numbers and I want to split those into two files. I am looking at something like:
./runme.sh | grep 'ook' >> ook.out | grep 'eek' >> eek.out
Where the second pipe should not, as is the case, take the output of the first grep but that of runme.sh. Is that possible?
5 Answers 5
That's perfect use case for the utility pee
.
./runme.sh | pee "grep ook >> ook.out" "grep eek >> eek.out"
In Debian & derivatives, pee
is found in moreutils
package.
-
that was good one minaev :-)Nikhil Mulley– Nikhil Mulley2011年12月16日 12:34:42 +00:00Commented Dec 16, 2011 at 12:34
-
1I have to say I think @Nikhil's answer is more versatile as you might not be able to
pee
on all systems.cwd– cwd2012年01月07日 15:12:09 +00:00Commented Jan 7, 2012 at 15:12 -
1Wow. Good luck to everyone googling man page.Victor Sergienko– Victor Sergienko2018年01月09日 19:21:07 +00:00Commented Jan 9, 2018 at 19:21
You should do egrep for both patterns then.
`/.runme.sh | egrep "ook|eek"
but it seems you need to redirect the each pattern evaluation output to its own file, which grep does not seem to support. Anyone, please correct me if it is possible.
Edit: minaev gave a working example with pee from moreutils, but if pee is missing on your platform, we can still use tee like this. Just play with process substitution.
./runme.sh |tee >(grep ook > ook.txt) >(grep eek > eek.txt)
Example:
[centos@centos scripts]$ ./runme.sh
eekfarapplebin
keeekmajowrwt
keekookjsfskooeek
ook
[centos@centos scripts]$ ./runme.sh | tee >(grep eek >eek.txt) >(grep ook >ook.txt)
eekfarapplebin
keeekmajowrwt
keekookjsfskooeek
ook
[centos@centos scripts]$ cat eek.txt
eekfarapplebin
keeekmajowrwt
keekookjsfskooeek
[centos@centos scripts]$ cat ook.txt
keekookjsfskooeek
ook
[centos@centos scripts]$
The simple awk
alternative:
./runme.sh | awk '/ook/{print>>"ook.out"}/eek/{print>>"eek.out"}'
With little addition the awk
code can be made easily extensible – just put in array r as many regular expression-output file pairs are needed:
./runme.sh | awk 'BEGIN{r["ook"]="ook.out";r["eek"]="eek.out"}{for(i in r)if(0ドル~i)print>>r[i]}'
The sed
w
command is equivalent of >
, sadly there is no way to append to file:
./runme.sh | sed -n $'/ook/wook.out\n/eek/week.out'
I'm guessing runme is either a long running command, or requires atomicity (runs once).
I would suggest creating a unique temp filename with mktemp
TMPFILE=$(mktemp)
./runme.sh > $TMPFILE
grep 'ook' $TMPFILE >> ook.out
grep 'eek' $TMPFILE >> eek.out
\rm -f $TMPFILE
-
See Nikhil's answer for a clever use of
tee
...Sardathrion - against SE abuse– Sardathrion - against SE abuse2011年12月16日 12:53:14 +00:00Commented Dec 16, 2011 at 12:53 -
1I saw that, so I stand corrected on the use of
tee
My old learn linux mantra - learn something new everyday, try it, practice it, remember it.bsd– bsd2011年12月16日 16:05:04 +00:00Commented Dec 16, 2011 at 16:05 -
@dbowning: Tru dat.Sardathrion - against SE abuse– Sardathrion - against SE abuse2011年12月16日 16:07:43 +00:00Commented Dec 16, 2011 at 16:07
The command tee can keep a copy of the piped stream in a temp file. Then we can grep the original stdout and also the temp file separately:
./runme.sh |tee temp |grep ook >>ook.out;grep eek temp >>eek.out
pee
appears to do just what I want.sed
andawk
can solve this in 1 process. Withpee
you will start 3 processes. Hack or not, sounds more efficient.