Clean way to duplicate stdin - for use by git pre-push?

Viewed 96

I want to pass stdin to two different processes. What are some nice clean ways to do this?

(I'm proposing a tempfile based way as an answer below, but maybe there are better ways?)

GitHub's "git lfs pre-push" pre-push hook reads stdin. I want to install a second pre-push check, e.g. the WIP check I find in pre-push.sample. This means I need to save stdin in some reasonable way, so that the commits can be read by both "git lfs pre-push" and by the "while read local_ref local_sha remote_ref remote_sha" loop.

2 Answers

You could use tee with an output process substitution.

Here I'll put the individual check code into functions for clarity:

check1() {
  git lfs pre-push "$@"
}

check2() {
  while read -ra variables; do
    : ...
  done
}

# redirecting to /dev/null so you don't get the original input as well
tee >(check1) >(check2) >/dev/null

I'm not sure of the limitations, but you can do an arbitrarily large number of checks this way:

tee >(check1) \
    >(check2) \
    >(check3) ...

A demo:

check1() { while IFS= read -r n; do echo $((n*5)); done; }
check2() { while IFS= read -r line; do echo ">>>$line<<<"; done; }
seq 101 100 1101 | tee >(check1) >(check2) >/dev/null

output

505
>>>101<<<
1005
1505
2005
2505
3005
3505
>>>201<<<
4005
4505
>>>301<<<
5005
>>>401<<<
5505
>>>501<<<
>>>601<<<
>>>701<<<
>>>801<<<
>>>901<<<
>>>1001<<<
>>>1101<<<

Since both checks are running in subshells, the output order is indeterminate.

One way (which I currently have in my git pre-push hook) is to save the input to a temporary file. The relevant lines from my .git/hooks/pre-push (skipping the unrelated ones):

TMPFILE=`mktemp`
tee $TMPFILE | git lfs pre-push "$@"
cat $TMPFILE | while read local_ref local_sha remote_ref remote_sha ; do
    # ...etc...
done
rm $TMPFILE

Just because the buffer with which the data is duplicated is temporarily given a filename, a handle in the filesystem, doesn't necessarily mean it's a dirty solution! ;-)

Related