There are no "rules" as such. Some programs take input from STDIN, and some do not. If a program can take input from STDIN, it can be piped to, if not, it can't.
You can normally tell whether a program will take input or not by thinking about what it does. If the program's job is to somehow manipulate the contents of a file (e.g. grep
, sed
, awk
etc.), it normally takes input from STDIN. If its job is to manipulate the file itself (e.g. mv
,rm
, cp
) or a process (e.g. kill
, lsof
) or to return information about something (e.g. top
, find
, ps
) then it doesn't.
Another way of thinking about it is the difference between arguments and input. For example:
mv foo bar
In the command above, mv
has no input as such. What it has been given is two arguments. It does not know or care what is in either of the files, it just knows those are its arguments and it should manipulate them.
On the other hand
sed -e 's/foo/bar/' < file
--- -- ------------ ----
| | | |-> input
| | |------------> argument
| |--------------------> option/flag/switch
|------------------------> command
Here, sed
has been given input as well as an argument. Since it takes input, it can read it from STDIN and it can be piped to.
It gets more complicated when an argument can be the input. For example
cat file
Here, file
is the argument that was given to cat
. To be precise, the file name file
is the argument. However, since cat
is a program that manipulates the contents of files, its input is whatever is inside file
.
This can be illustrated using strace
, a program that tracks the system calls made by processes. If we run cat foo
via strace
, we can see that the file foo
is opened:
$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)
The first line above shows that the program /bin/cat
was called and its arguments were cat
and foo
(the first argument is always the program itself). Later on, the argument foo
was opened in read only mode. Now, compare this with
$ strace ls foo 2| grep foo
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo
Here also, ls
took itself and foo
as arguments. However, there is no open
call, the argument is not treated as input. Instead, ls
calls the system's stat
library (which is not the same thing as the stat
command) to get information about the file foo
.
In summary, if the command you're running will read its input, you can pipe to it, if it doesn't, you can't.
pgrep
,pkill
andkillall
commands.pgrep
and the rest can achieve this perfectly :)