I'm guessing this has nothing to do with ack
but more with bash
:
Here we create file.txt
containing the string foobar
soack
can find foobar
in it:
> echo foobar > file.txt
> echo 'ack foobar file.txt' > ack.sh
> bash ack.sh
foobar
> bash < ack.sh
foobar
So far so good. But why doesn't ack find anything in it like this?
> cat ack.sh | bash
(no output)
or
> echo 'ack foobar file.txt' | bash
(no output)
Why doesn't ack
find foobar
in the last two cases?
Adding unbuffer
(from expect) in front makes it work, which I don't understand:
> echo 'unbuffer ack foobar file.txt' | bash
foobar
Even stranger:
> cat ack2.sh
echo running
ack foobar file.txt
echo running again
unbuffer ack foobar file.txt
# Behaves as I'd expect
> bash ack2.sh
running
foobar
running again
foobar
# Strange output
> cat ack2.sh | bash
running
unbuffer ack foobar file.txt
What's up with this output? It echos unbuffer ack foobar file.txt
but not running again
? Huh?
ack
gets confused because stdin is a pipe rather than a terminal. You need to pass the --nofilter
option to force ack
to treat stdin as a tty.
This:
# ack.sh
ack --nofilter foobar file.txt
works:
$ cat ack.sh | bash
foobar
If you ask me, that behaviour is quite unexpected. Probably it is expected when someone understand the concepts of ack
which I do not atm. I would expect that ack
doesn't look at stdin when filename arguments are passed to it.
Why does unbuffer
"solve" the problem?
unbuffer
, following it's man page, does not attempt to read from stdin:
Normally, unbuffer does not read from stdin. This simplifies use of unbuffer in some situations. To use unbuffer in a pipeline, use the -p flag. ...
Looks like ack
tries to be too! smart about stdin here. If it is empty it does not read from stdin
and looks at the filenames passed to it. Again, imo it would be correct to not look at stdin at all if filename arguments are present.