Por que alguns comandos não lêem a entrada padrão?

18

Eu me pergunto o que quando devemos usar pipeline e quando não deveríamos.

Digamos, por exemplo, que para matar determinados processos que manipulam arquivos PDF, o seguinte não funcionará usando o pipeline:

ps aux | grep pdf | awk '{print $2}'|kill

Em vez disso, só podemos fazer isso das seguintes maneiras:

kill $(ps aux| grep pdf| awk '{print $2}')

ou

ps aux | grep pdf | awk '{print $2}'| xargs kill

De acordo com man bash (versão 4.1.2 ):

The standard output of command is connected via a pipe to the standard input of command2.

Para o cenário acima:

  • o stdin de grep é o stdout de ps . Isso funciona.
  • o stdin de awk é o stdout de grep . Isso funciona.
  • o stdin de kill é o stdout de awk . Isso não funciona.

O stdin do comando a seguir está sempre recebendo entrada do stdout do comando anterior.

  • Por que não funciona com kill ou rm ?
  • Qual é a diferença entre kill , rm entrada com grep , awk entrada?
  • Existem regras?
por sylye 03.07.2014 / 12:06

3 respostas

16

Existem duas maneiras comuns de fornecer entradas para programas:

  • fornece dados para STDIN dos processos
  • especificar argumentos da linha de comando

kill usa apenas argumentos de linha de comando. Não lê de STDIN. Programas como grep e awk lêem a partir de STDIN (se nenhum nome de arquivo é fornecido como argumento de linha de comando) e processam os dados de acordo com seus argumentos de linha de comando (padrão, instruções, sinalizadores, ...).

Você só pode canalizar para STDIN de outros processos, não para argumentos de linha de comando.

A regra comum é que os programas usam STDIN para processar uma quantidade arbitrária de dados. Todos os parâmetros de entrada extras ou, se houver apenas alguns, são passados pelos argumentos da linha de comando. Se a linha de comando puder ficar muito longa, por exemplo, para longos textos do programa awk , muitas vezes há a possibilidade de lê-los a partir de arquivos de programa extras ( -f opção de awk ).

Para usar o STDOUT de programas como argumentos de linha de comando, use $(...) ou no caso de muitos dados xargs . find também pode isso diretamente com -exec ... {} + .

Para completar: Para escrever argumentos de linha de comando para STDOUT, use echo .

    
por 03.07.2014 / 12:14
13

Esta é uma questão interessante e trata de uma parte da filosofia Unix / Linux.

Então, qual é a diferença entre programas como grep , sed , sort de um lado e kill , rm , ls de outro lado? Eu vejo dois aspectos.

O aspecto filtro

  • O primeiro tipo de programa também é chamado de filtros . Eles pegam uma entrada, seja de um arquivo ou de STDIN, modificam-na e geram alguma saída, principalmente para STDOUT. Eles devem ser usados em um canal com outros programas como fontes e destinos.

  • O segundo tipo de programas atua em uma entrada, mas a saída que eles fornecem geralmente não está relacionada à entrada. kill não tem saída quando funciona regularmente, nem ls . O apenas tem um valor de retorno para mostrar sucesso. Eles normalmente não recebem entrada de STDIN, mas principalmente fornecem saída para STDOUT.

Para programas como ls , o aspecto do filtro não funciona tão bem. Ele pode certamente ter uma entrada (mas não precisa de uma) e a saída está intimamente relacionada a essa entrada, mas não funciona como um filtro. No entanto, para esse tipo de programa, o outro aspecto ainda funciona:

O aspecto semântico

  • Para filtros, sua entrada tem sem significado semântico . Eles apenas lêem dados, modificam dados, dados de saída. Não importa se esta é uma lista de valores numéricos, alguns nomes de arquivos ou código-fonte HTML. O significado desses dados é fornecido somente pelo código você para o filtro: o regex para grep , as regras para awk ou o programa Perl.

  • Para outros programas, como kill ou ls , a entrada deles tem um significado , uma denotação . kill espera números de processos, ls espera nomes de arquivos ou caminhos. Eles não podem manipular dados arbitrários e eles não são destinados a. Muitos deles nem precisam de nenhuma entrada ou parâmetros, como ps . Eles normalmente não lêem de STDIN.

Poder-se-ia provavelmente combinar estes dois aspectos: Um filtro é um programa cuja entrada não tem um significado semântico para o programa.

Tenho certeza de que li sobre essa filosofia em algum lugar, mas não me lembro de nenhuma fonte no momento, desculpe. Se alguém tiver algumas fontes presentes, sinta-se à vontade para editar.

    
por 03.07.2014 / 12:39
4

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.

    
por 03.07.2014 / 13:12