Não há "regras" como tal. Alguns programas recebem entrada de STDIN e outros não. Se um programa pode receber entrada de STDIN, pode ser canalizado para, se não, não pode.
Você pode dizer normalmente se um programa aceitará ou não a entrada, pensando sobre o que faz. Se o trabalho do programa for de alguma forma manipular o conteúdo de um arquivo (por exemplo, grep , sed , awk etc.), ele normalmente recebe entrada de STDIN. Se o trabalho for manipular o arquivo em si (por exemplo, mv , rm , cp ) ou um processo (por exemplo, kill , lsof ) ou para retornar informações sobre algo (por exemplo, top , find , ps ) então isso não acontece.
Outra maneira de pensar sobre isso é a diferença entre argumentos e entrada. Por exemplo:
mv foo bar
No comando acima, mv não tem entrada como tal. O que foi dado é dois argumentos. Ele não sabe ou se importa com o que está em nenhum dos arquivos, ele apenas sabe quais são seus argumentos e deve manipulá-los.
Por outro lado
sed -e 's/foo/bar/' < file
--- -- ------------ ----
| | | |-> input
| | |------------> argument
| |--------------------> option/flag/switch
|------------------------> command
Aqui, sed recebeu entrada e também um argumento. Uma vez que é necessário inserir, ele pode lê-lo a partir de STDIN e pode ser canalizado para.
Fica mais complicado quando um argumento pode ser a entrada. Por exemplo
cat file
Aqui, file é o argumento que foi dado a cat . Para ser preciso, o arquivo nome file é o argumento. No entanto, como cat é um programa que manipula o conteúdo dos arquivos, sua entrada é o que estiver dentro de file .
Isso pode ser ilustrado usando strace , um programa que rastreia as chamadas do sistema feitas por processos. Se executarmos cat foo via strace , poderemos ver que o arquivo foo está aberto:
$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)
A primeira linha acima mostra que o programa /bin/cat foi chamado e seus argumentos foram cat e foo (o primeiro argumento é sempre o próprio programa). Mais tarde, o argumento foo foi aberto no modo somente leitura. Agora, compare isso com
$ 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
Aqui também, ls tomou a si mesmo e foo como argumentos. No entanto, não há open call, o argumento não é tratado como entrada. Em vez disso, ls chama a biblioteca stat do sistema (que não é a mesma coisa que o comando stat ) para obter informações sobre o arquivo foo .
Em resumo, se o comando que você estiver executando for ler sua entrada, você pode redirecioná-lo para ele, se não, você não pode.