Isso provavelmente está em muitas perguntas frequentes - em vez de usar:
cat file | command
(que é chamado de uso inútil de gato), a maneira correta deve ser:
command < file
Na segunda maneira, "correta" - o SO não precisa gerar um processo extra.
Apesar de saber disso, continuei a usar gato inútil por 2 motivos.
mais estético - gosto quando os dados se movem uniformemente apenas da esquerda para a direita. E é mais fácil de substituir
cat
com algo mais (gzcat
,echo
...), adicionar um segundo arquivo ou inserir novo filtro (pv
,mbuffer
,grep
...).Eu "senti" que poderia ser mais rápido em alguns casos. Mais rápido porque há 2 processos, o primeiro (
cat
) faz a leitura e o segundo faz tudo. E eles podem ser executados em paralelo, o que às vezes significa uma execução mais rápida.
Minha lógica está correta (pelo segundo motivo)?
fonte
cat
é um tubo de identidade . Ele apenas transmite sua entrada para sua saída. Se o segundo programa na cadeia pode obter sua entrada do mesmo argumento que você passoucat
(ou da entrada padrão, se você não passar nenhum argumento), entãocat
é absolutamente inútil e apenas resulta em um processo adicional sendo bifurcado e um canal adicional sendo criada.-
, é um tubo de identidade. Quando ele tem mais de um argumento de nome de arquivo sem traço, ele se torna algo mais do que um canal de identidade e começa a servir a um propósito real.<file command1 | command2
, embora haja desacordo sobre a estética.Respostas:
Eu não sabia do prêmio até hoje, quando um novato tentou apontar o UUOC para mim por uma das minhas respostas. Era um
cat file.txt | grep foo | cut ... | cut ...
. Eu dei a ele um pedaço do que eu pensava, e só depois de fazer isso visitei o link que ele me deu referindo-se à origem do prêmio e à prática de fazê-lo. Pesquisas adicionais me levaram a esta pergunta Infelizmente, apesar da consideração consciente, nenhuma das respostas incluiu meu raciocínio.Eu não queria ficar na defensiva ao responder a ele. Afinal, na minha juventude, eu teria escrito o comando
grep foo file.txt | cut ... | cut ...
porque sempre que você faz os simplesgrep
s frequentes, você aprende a localização do argumento do arquivo e já sabe que o primeiro é o padrão e os últimos são os nomes dos arquivos.Foi uma escolha consciente a ser usada
cat
quando respondi à pergunta, em parte por uma razão de "bom gosto" (nas palavras de Linus Torvalds), mas principalmente por uma razão convincente de função.O último motivo é mais importante, então irei divulgá-lo primeiro. Quando eu ofereço um pipeline como solução, espero que ele seja reutilizável. É bastante provável que um pipeline seja adicionado no final ou unido em outro pipeline. Nesse caso, ter um argumento de arquivo para grep atrapalha a capacidade de reutilização, e muito possivelmente fazê-lo silenciosamente sem uma mensagem de erro se o argumento de arquivo existir. I. e.
grep foo xyz | grep bar xyz | wc
lhe dará quantas linhas emxyz
contêmbar
enquanto você espera o número de linhas que contêmfoo
ebar
. Ter que alterar os argumentos de um comando em um pipeline antes de usá-lo está sujeito a erros. Acrescente a isso a possibilidade de falhas silenciosas e torna-se uma prática particularmente insidiosa.A primeira razão também não é sem importância, uma vez que muito " bom gosto " é meramente uma razão subconsciente intuitiva para coisas como as falhas silenciosas acima, nas quais você não consegue pensar direito no momento em que alguma pessoa que precisa de educação diz "mas não é aquele gato inútil ".
No entanto, tentarei também tornar consciente o antigo motivo do "bom gosto" que mencionei. Essa razão tem a ver com o espírito de design ortogonal do Unix.
grep
não fazcut
els
não fazgrep
. Portanto, no mínimogrep foo file1 file2 file3
vai contra o espírito do design. A maneira ortogonal de fazer isso écat file1 file2 file3 | grep foo
. Agora,grep foo file1
é apenas um caso especial degrep foo file1 file2 file3
, e se você não tratá-lo da mesma forma, você está pelo menos usando os ciclos do relógio cerebral tentando evitar o prêmio do gato inútil.Isso nos leva ao argumento de que
grep foo file1 file2 file3
está concatenando, ecat
concatena de forma apropriada,cat file1 file2 file3
mas porquecat
não está concatenando,cat file1 | grep foo
portanto, estamos violando o espírito docat
e do todo-poderoso Unix. Bem, se fosse esse o caso, então o Unix precisaria de um comando diferente para ler a saída de um arquivo e despejá-lo em stdout (não paginá-lo ou qualquer coisa apenas um spit puro em stdout). Portanto, você teria a situação em que dizcat file1 file2
ou dizdog file1
e conscienciosamente lembre-se de evitarcat file1
para evitar o recebimento do prêmio, ao mesmo tempo que evita,dog file1 file2
uma vez que, esperançosamente, o design dedog
geraria um erro se vários arquivos fossem especificados.Esperançosamente, neste ponto, você simpatiza com os designers do Unix por não incluir um comando separado para cuspir um arquivo para stdout, enquanto também nomeia
cat
para concatenar em vez de dar a ele algum outro nome.<edit>
removeu comentários incorretos sobre<
, de fato,<
é um recurso eficiente de não copiar para cuspir um arquivo em stdout que você pode posicionar no início de um pipeline, então os designers do Unix incluíram algo especificamente para isso</edit>
A próxima pergunta é: por que é importante ter comandos que apenas geram um arquivo ou a concatenação de vários arquivos para o stdout, sem nenhum processamento posterior? Uma razão é evitar que cada comando Unix que opera na entrada padrão saiba como analisar pelo menos um argumento do arquivo de linha de comando e usá-lo como entrada, se existir. A segunda razão é evitar que os usuários tenham que se lembrar: (a) para onde vão os argumentos do nome do arquivo; e (b) evitar o bug do pipeline silencioso, conforme mencionado acima.
Isso nos leva ao porquê de
grep
ter uma lógica extra. A lógica é permitir a fluência do usuário para comandos que são usados com frequência e de forma autônoma (em vez de um pipeline). É um pequeno compromisso de ortogonalidade para um ganho significativo em usabilidade. Nem todos os comandos devem ser projetados dessa forma e os comandos que não são usados com frequência devem evitar completamente a lógica extra de argumentos de arquivo (lembre-se de que a lógica extra leva a uma fragilidade desnecessária (a possibilidade de um bug)). A exceção é permitir argumentos de arquivo como no caso degrep
. (A propósito, observe quels
há uma razão completamente diferente para não apenas aceitar, mas também exigir argumentos de arquivo)Finalmente, o que poderia ter sido feito melhor seria se comandos excepcionais como
grep
(mas não necessariamentels
) gerassem um erro se a entrada padrão também estivesse disponível quando os argumentos do arquivo fossem especificados.fonte
grep
é chamado com vários nomes de arquivo, ele prefixa as linhas encontradas com o nome do arquivo em que foi encontrado (a menos que você desative esse comportamento). Ele também pode relatar os números de linha nos arquivos individuais. Se usar apenascat
para alimentargrep
, você perde os nomes dos arquivos e os números das linhas são contínuos em todos os arquivos, não por arquivo. Portanto, há razões paragrep
lidar com vários arquivos quecat
não podem ser manipulados. Os casos de arquivo único e nenhum arquivo são simplesmente casos especiais do uso geral de vários arquivosgrep
.< file command1 ...
. Embora a posição convencional para os operadores de redirecionamento de E / S seja após o nome do comando e seus argumentos, essa é apenas a convenção e não um posicionamento obrigatório. O<
tem que preceder o nome do arquivo. Então, há um perto de perfeita simetria entre>output
e<input
redirecionamentos:<input command1 -opt 1 | command2 -o | command3 >output
.cat
é inútil. Não é quecat
seja inútil; é que uma construção particular não precisa do uso decat
. Se quiser, observe que é UUoC (Uso inútil decat
) e não UoUC (Uso inútilcat
). Existem muitas ocasiões em quecat
é a ferramenta correta a ser usada; Não tenho nenhum problema com ele sendo usado quando é a ferramenta correta a ser usada (e, de fato, menciono um caso em minha resposta).cat
no canal pode não ser um grande negócio, dependendo dos dados, mas quando usado como um ambiente de programação pode ser absolutamente necessário implementar essas coisas críticas de desempenho; especialmente quando se lida combash
isso, em termos de desempenho, é como uma roda de formato retangular (em comparação comksh
qualquer coisa. Estou falando até 10 vezes mais lento aqui - sem brincadeira). Você não quer otimizar seus garfos (e não apenas isso) quando se lida com scripts maiores ou enormes loops.Não!
Em primeiro lugar, não importa onde em um comando o redirecionamento acontece. Portanto, se você gosta do redirecionamento para a esquerda do comando, tudo bem:
é o mesmo que
Em segundo lugar, existem n + 1 processos e um subshell acontecendo quando você usa um pipe. É decididamente mais lento. Em alguns casos, n seria zero (por exemplo, quando você está redirecionando para um shell embutido), então, ao usar,
cat
você está adicionando um novo processo totalmente desnecessário.Como generalização, sempre que você estiver usando um cano, vale a pena gastar 30 segundos para ver se você consegue eliminá-lo. (Mas provavelmente não vale a pena demorar muito mais do que 30 segundos.) Aqui estão alguns exemplos em que tubos e processos são usados com frequência desnecessariamente:
Sinta-se à vontade para editar e adicionar mais exemplos.
fonte
< cat grep dog
é um exemplo inventado para mostrar que você não pode distinguir facilmente entre o arquivo de entrada, o comando que recebe a entrada e os argumentos para o comando.stdout=$(foo bar -exec baz <qux | ENV=VAR quux)
. P. Aplica-<qux
se afoo
, ou abaz
, que é-exec
'd byfoo
? R. Aplica-se afoo
, mas pode parecer ambíguo. Colocar<qux
antesfoo
, neste caso, é mais claro, embora menos comum, e é análogo ao trailingENV=VAR quux
.<"cat" grep dog
é mais fácil de ler, aí. (Normalmente sou pró-espaço em branco, mas este caso específico é uma exceção).Eu discordo da maioria dos casos do prêmio UUOC excessivamente presunçoso porque, ao ensinar outra pessoa,
cat
é um marcador conveniente para qualquer comando ou pipeline complicado de comandos que produza saída adequada para o problema ou tarefa em discussão.Isso é especialmente verdadeiro em sites como Stack Overflow, ServerFault, Unix e Linux ou qualquer um dos sites SE.
Se alguém perguntar especificamente sobre otimização, ou se você quiser adicionar informações extras sobre isso, ótimo, fale sobre como usar cat é ineficiente. Mas não repreenda as pessoas porque elas escolheram buscar simplicidade e facilidade de compreensão em seus exemplos, em vez de olhar para mim como sou legal! complexidade.
Resumindo, porque o gato nem sempre é gato.
Também porque a maioria das pessoas que gosta de premiar UUOCs o faz porque estão mais preocupadas em se exibir sobre o quão 'inteligentes' são do que em ajudar ou ensinar as pessoas. Na realidade, eles demonstram que provavelmente são apenas mais um novato que encontrou um pequeno graveto para bater em seus colegas.
Atualizar
Aqui está outro UUOC que postei em uma resposta em https://unix.stackexchange.com/a/301194/7696 :
Pedantes UUOC diriam que é um UUOC porque é facilmente possível tornar
$filter
padrão para a string vazia e fazer com que aif
instrução o faça,filter='| grep -v "^$"'
mas IMO, por não incorporar o caractere de barra vertical$filter
, este "inútil"cat
serve ao propósito extremamente útil de autodocumentar o fato que$filter
naprintf
linha não é apenas outro argumento parasqlplus
, é um filtro de saída opcional selecionável pelo usuário.Se houver necessidade de vários filtros de saída opcionais, o processamento de opções pode apenas anexar com
| whatever
a$filter
frequência necessária - um extracat
no pipeline não vai prejudicar nada ou causar qualquer perda perceptível de desempenho.fonte
==
dentro[ ]
não é especificado pelo POSIX, e nem todas as implementações o aceitam. O operador padronizado é justo=
.Com a versão UUoC,
cat
deve ler o arquivo na memória, depois gravá-lo no pipe, e o comando deve ler os dados do pipe, então o kernel tem que copiar o arquivo inteiro três vezes, enquanto no caso redirecionado, o kernel só precisa copiar o arquivo uma vez. É mais rápido fazer algo uma vez do que três vezes.Usando:
é um uso totalmente diferente e não necessariamente inútil de
cat
. Ainda é inútil se o comando for um filtro padrão que aceita zero ou mais argumentos de nome de arquivo e os processa sucessivamente. Considere otr
comando: é um filtro puro que ignora ou rejeita argumentos de nome de arquivo. Para alimentar vários arquivos nele, você deve usarcat
como mostrado. (Claro, há uma discussão separada de que o design dotr
não é muito bom; não há nenhuma razão real para ele não ter sido projetado como um filtro padrão.) Isso também pode ser válido se você quiser que o comando trate todas as entradas como um arquivo único em vez de vários arquivos separados, mesmo se o comando aceitar vários arquivos separados: por exemplo,wc
é esse comando.É o
cat single-file
caso que é incondicionalmente inútil.fonte
Em defesa do gato:
Sim,
ou
é mais eficiente, mas muitas invocações não têm problemas de desempenho, então você não se importa.
razões ergonômicas:
Estamos acostumados a ler da esquerda para a direita, então um comando como
é trivial de entender.
tem que pular o processo 1 e depois ler da esquerda para a direita. Isso pode ser curado por:
parece de alguma forma, como se houvesse uma seta apontando para a esquerda, onde nada está. Mais confuso e parecendo uma citação sofisticada:
e gerar scripts geralmente é um processo iterativo,
onde você vê seu progresso passo a passo, enquanto
nem mesmo funciona. As formas simples são menos propensas a erros e a catenação ergonômica do comando é simples com o gato.
Outro tópico é que a maioria das pessoas foi exposta a> e <como operadores de comparação, muito antes de usar um computador e ao usar um computador como programadores, é muito mais frequentemente exposta a estes como tal.
E comparar dois operandos com <e> é contra comutativo, o que significa
Lembro-me da primeira vez que usei <para redirecionamento de entrada, temi
pode significar o mesmo que
e de alguma forma sobrescrever meu script a.sh. Talvez este seja um problema para muitos iniciantes.
diferenças raras
Este último pode ser usado em cálculos diretamente.
Claro que o <também pode ser usado aqui, em vez de um parâmetro de arquivo:
mas quem se importa - 15k?
Se eu ocasionalmente tivesse problemas, certamente mudaria meu hábito de invocar gatos.
Ao usar arquivos muito grandes ou muitos, muitos, evitar gato é bom. Para a maioria das perguntas, o uso de gato é ortogonal, fora do tópico, não é um problema.
Começar esse uso inútil de discussão sobre gatos em cada segundo tópico de shell é apenas chato e chato. Pegue uma vida e espere pelo seu minuto de fama, ao lidar com questões de desempenho.
fonte
file > a.sh
si só vale a pena ler isto :) Obrigado por compartilhar!cat file | wc -c
,wc
precisa ler stdin até EOF, contando bytes. Mas nisso,wc -c < file
ele apenas estatísticas stdin, descobre que é um arquivo normal e imprime st_size em vez de ler qualquer entrada. Para um arquivo grande, a diferença no desempenho seria claramente visível.Um problema adicional é que o tubo pode mascarar silenciosamente uma subcamada. Para este exemplo, substituirei
cat
porecho
, mas existe o mesmo problema.Você pode esperar
x
conterfoo
, mas não o faz. O quex
você definiu estava em um subshell gerado para executar owhile
loop.x
no shell que iniciou o pipeline tem um valor não relacionado ou não está definido.No bash4, você pode configurar algumas opções de shell para que o último comando de um pipeline seja executado no mesmo shell daquele que inicia o pipeline, mas então você pode tentar isso
e
x
é mais uma vez local para owhile
subshell de.fonte
shopt -s lastpipe
evitar a criação do subshell.Como alguém que regularmente aponta isso e uma série de outros antipadrões de programação de shell, me sinto obrigado, tardiamente, a ponderar.
Shell script é basicamente uma linguagem de copiar / colar. Para a maioria das pessoas que escrevem scripts de shell, elas não querem aprender a linguagem; é apenas um obstáculo que eles precisam superar para continuar a fazer as coisas no (s) idioma (s) com os quais estão realmente familiarizados.
Nesse contexto, vejo como perturbador e potencialmente até destrutivo propagar vários anti-padrões de script de shell. O código que alguém encontra no Stack Overflow deve ser idealmente possível para copiar / colar em seu ambiente com alterações mínimas e compreensão incompleta.
Entre os muitos recursos de script de shell na rede, Stack Overflow é incomum porque os usuários podem ajudar a moldar a qualidade do site editando as perguntas e respostas no site. No entanto, as edições de código podem ser problemáticas porque é fácil fazer alterações que não foram pretendidas pelo autor do código. Portanto, tendemos a deixar comentários para sugerir alterações no código.
O UUCA e comentários antipadrões relacionados não são apenas para os autores do código que comentamos; eles são um caveat emptor para ajudar os leitores do site a se conscientizarem dos problemas no código que encontram aqui.
Não podemos esperar alcançar uma situação em que nenhuma resposta no Stack Overflow recomende
cat
s inúteis (ou variáveis não citadas, ouchmod 777
, ou uma grande variedade de outras pragas antipadrão), mas podemos pelo menos ajudar a educar o usuário que está prestes a copiar / cole esse código no loop mais interno de seu script, que é executado milhões de vezes.No que diz respeito às razões técnicas, a sabedoria tradicional é que devemos tentar minimizar o número de processos externos; isso continua sendo uma boa orientação geral ao escrever scripts de shell.
fonte
cat
é um monte de mudanças de contexto extras e largura de banda de memória (e poluição do cache L3 de cópias extras de dados nocat
buffer de leitura e nos buffers de pipe). Especialmente em uma grande máquina com vários núcleos (como muitas configurações de hospedagem), a largura de banda de cache / memória é um recurso compartilhado.bzip2
e agzip
compressão são ambas muito lentas em comparação com a quantidade de sobrecargacat
adicionada somente a isso (com a máquina ociosa). É difícil ler suas tabelas (quebra de linha no meio de um número?).sys
o tempo aumenta muito, mas ainda pequeno vs. usuário ou real?Costumo usar
cat file | myprogram
em exemplos. Às vezes estou sendo acusado de uso inútil de gato ( http://porkmail.org/era/unix/award.html ). Eu discordo pelos seguintes motivos:É fácil entender o que está acontecendo.
Ao ler um comando UNIX, você espera um comando seguido de argumentos seguidos de redirecionamento. É possível colocar o redirecionamento em qualquer lugar, mas raramente é visto - portanto, as pessoas terão mais dificuldade em ler o exemplo. Acredito
é mais fácil de ler do que
Se você mover o redirecionamento para o início, estará confundindo as pessoas que não estão acostumadas com esta sintaxe:
e os exemplos devem ser fáceis de entender.
É fácil mudar.
Se você sabe que o programa pode ler
cat
, normalmente pode assumir que ele pode ler a saída de qualquer programa com saída para STDOUT e, portanto, pode adaptá-lo às suas próprias necessidades e obter resultados previsíveis.Salienta que o programa não falha, se STDIN não for um arquivo.
Não é seguro presumir que, se
program1 < foo
funcionar,cat foo | program1
também funcionará. No entanto, é seguro assumir o oposto. Este programa funciona se STDIN for um arquivo, mas falha se a entrada for um canal, porque usa a busca:Custo de desempenho
Há um custo para fazer o adicional
cat
. Para dar uma ideia de quanto eu executei alguns testes para simular a linha de base (cat
), baixa taxa de transferência (bzip2
), média (gzip
) e alta taxa de transferência (grep
).Os testes foram executados em um sistema low end (0,6 GHz) e um laptop comum (2,2 GHz). Eles foram executados 10 vezes em cada sistema e o melhor tempo foi escolhido para simular a situação ideal para cada teste. O $ ISO era ubuntu-11.04-desktop-i386.iso. (Tabelas mais bonitas aqui: http://oletange.blogspot.com/2013/10/useless-use-of-cat.html )
Os resultados mostram que para baixa e média vazão o custo é da ordem de 1%. Isso está dentro da incerteza das medições, portanto, na prática, não há diferença.
Para alto rendimento, a diferença é maior e há uma diferença clara entre os dois.
Isso leva à conclusão: você deve usar em
<
vez decat |
se:Caso contrário, não importa se você usa
<
oucat |
.E, portanto, você só deve dar um prêmio UUoC se e somente se:
fonte
Acho que (da forma tradicional) usar cachimbo é um pouco mais rápido; na minha caixa eu usei
strace
comando para ver o que está acontecendo:Sem tubo:
E com cachimbo:
Você pode fazer alguns testes
strace
etime
comandos com mais e mais comandos para um bom benchmarking.fonte
strace
mostra que é mais rápido -strace
não está rastreando awc -l
execução no segundo caso. Ele rastreia apenas o primeiro comando do pipeline aqui.strace -f sh -c 'wc -l < wrong_output.c'
ao ladostrace -f sh -c 'cat wrong_output.c | wc -l'
.cat
: ideone.com/2w1W42#stderrmkfifo
cria um canal nomeado . Um tubo anônimo é configurado compipe(2)
bifurcação e fazendo com que o pai e o filho fechem as extremidades do tubo. Mas sim, essa resposta é um total absurdo, e nem mesmo tentei contar as chamadas do sistema ou usarstrace -O
para medir a sobrecarga, ou-r
para registrar a data e hora de cada chamada em relação à última ...