Muitas pessoas usam oneliners e scripts que contêm código ao longo das linhas
cat "$MYFILE" | command1 | command2 > "$OUTPUT"
O primeiro cat
é freqüentemente chamado de "uso inútil de gato", porque tecnicamente requer o início de um novo processo (geralmente /usr/bin/cat
) onde isso poderia ser evitado se o comando tivesse sido executado.
< "$MYFILE" command1 | command2 > "$OUTPUT"
porque o shell só precisa iniciar command1
e simplesmente apontar stdin
para o arquivo fornecido.
Por que o shell não faz essa conversão automaticamente? Eu sinto que a sintaxe "uso inútil de gato" é mais fácil de ler e o shell deve ter informações suficientes para se livrar automaticamente do gato inútil. Como cat
é definido no padrão POSIX, o shell deve implementá-lo internamente, em vez de usar um caminho binário. O shell pode até conter implementação apenas para exatamente uma versão de argumento e fallback para binário no caminho.
fonte
lseek
ainda é um comportamento definido e pode causar um resultado diferente, o comportamento de bloqueio diferente pode ser semanticamente significativo etc. Seria permitido fazer a alteração se você soubesse quais eram os outros comandos e sabia que eles não se importavam, ou se você simplesmente não se importava com a compatibilidade nesse nível, mas o benefício é bem pequeno. Imagino que a falta de benefício conduz a situação mais do que o custo de conformidade.cat
, ou qualquer outro utilitário. Também é permitido saber como os outros utilitários que pertencem ao sistema funcionam (por exemplo, ele pode saber como se comporta agrep
implementação externa que acompanha o sistema ). Isso é completamente viável, por isso é perfeitamente justo imaginar por que eles não o fazem.grep
. Esed
. Eawk
. Edu
. E quantas centenas, senão milhares de outros utilitários?Respostas:
Os 2 comandos não são equivalentes: considere o tratamento de erros:
cat <file that doesn't exist> | less
produzirá um fluxo vazio que será passado para o programa canalizado ... assim, você acaba com uma exibição que não mostra nada.< <file that doesn't exist> less
falhará ao abrir a barra e, em seguida, não abrirá menos.Tentar alterar o primeiro para o último pode interromper qualquer número de scripts que esperam executar o programa com uma entrada potencialmente em branco.
fonte
cat
sempre executará o segundo comando no pipeline, enquanto a variante com apenas redirecionamento de entrada não executará o comando se o arquivo de entrada estiver ausente.<"missing-file" grep foo | echo 2
não será executado,grep
mas será executadoecho
."Uso inútil de
cat
" é mais sobre como você escreve seu código do que sobre o que realmente é executado quando você executa o script. É um tipo de design anti-padrão , uma maneira de ir sobre algo que provavelmente poderia ser feito de uma forma mais eficiente. É uma falha no entendimento de como combinar melhor as ferramentas fornecidas para criar uma nova ferramenta. Eu diria que amarrar váriossed
e / ouawk
comandos juntos em um pipeline também pode às vezes ser considerado um sintoma desse mesmo antipadrão.A correção de instâncias de "uso inútil de
cat
" em um script é principalmente uma questão de corrigir o código-fonte do script manualmente. Uma ferramenta como o ShellCheck pode ajudar com isso, apontando os casos óbvios:Conseguir que o shell faça isso automaticamente seria difícil devido à natureza dos scripts do shell. A maneira como um script é executado depende do ambiente herdado de seu processo pai e da implementação específica dos comandos externos disponíveis.
O shell não sabe necessariamente o que
cat
é. Pode ser potencialmente qualquer comando de qualquer lugar da sua$PATH
função ou.Se fosse um comando interno (que pode estar em alguns shells), ele teria a capacidade de reorganizar o pipeline, pois conheceria a semântica de seu
cat
comando interno. Antes de fazer isso, seria necessário fazer suposições sobre o próximo comando no pipeline, após o originalcat
.Observe que a leitura da entrada padrão se comporta de maneira um pouco diferente quando conectada a um pipe e a um arquivo. Um tubo não pode ser procurado; portanto, dependendo do que o próximo comando no pipeline faz, ele pode ou não se comportar de maneira diferente se o pipeline foi reorganizado (pode detectar se a entrada é procurável e decidir fazer as coisas de forma diferente se for ou se não é, de qualquer forma, se comportaria de maneira diferente).
Essa pergunta é semelhante (em um sentido muito geral) a " Existem compiladores que tentam corrigir erros de sintaxe por conta própria? " (No site Software Engineering StackExchange), embora essa pergunta seja obviamente sobre erros de sintaxe, não padrões de design inúteis . A idéia de alterar automaticamente o código com base na intenção é basicamente a mesma.
fonte
cat
é e os outros comandos no pipeline (a regra como se) e se comportar de acordo, eles simplesmente não estão aqui porque é inútil e muito difícil.cat /dev/tty
é o interessante com o qual seria diferente<
.cat
comando realmente faz sem executá-lo . Pelo que você sabe (e o shell), o OP possui um comandocat
em seu caminho, que é uma simulação interativa de gatos, "myfile" é apenas o estado do jogo armazenadocommand1
ecommand2
está pós-processando algumas estatísticas sobre a atual sessão de reprodução ...Porque não é inútil.
No caso de
cat file | cmd
, o fd0
(stdin) decmd
será um pipe e, no casocmd <file
dele, poderá ser um arquivo, dispositivo normal etc.Um canal possui semântica diferente de um arquivo regular e sua semântica não é um subconjunto daquelas de um arquivo regular:
um arquivo regular não pode ser
select(2)
editado oupoll(2)
editado de maneira significativa; umselect(2)
sempre retornará "pronto". Interfaces avançadas comoepoll(2)
no Linux simplesmente não funcionam com arquivos regulares.no Linux há chamadas de sistema (
splice(2)
,vmsplice(2)
,tee(2)
), que só funcionam em tubos [1]Como
cat
é muito usado, ele pode ser implementado como um shell embutido, o que evitará um processo extra, mas assim que você iniciar esse caminho, a mesma coisa poderá ser feita com a maioria dos comandos - transformar o shell em um mais lento e desajeitadoperl
oupython
. provavelmente é melhor escrever outra linguagem de script com uma sintaxe semelhante a um pipe fácil de usar para continuações ;-)[1] Se você quer um exemplo simples não confeccionados para a ocasião, você pode olhar para o meu "binary exec do stdin" git essência com algumas explicações no comentário aqui . Implementar
cat
dentro dele para fazê-lo funcionar sem UUoC o tornaria 2 ou 3 vezes maior.fonte
cat
internos.cat /dev/urandom | cpu_bound_program
executa asread()
chamadas do sistema em um processo separado. No Linux, por exemplo, o trabalho real da CPU de gerar mais números aleatórios (quando o pool está vazio) é feito nessa chamada do sistema; portanto, o uso de um processo separado permite que você aproveite um núcleo separado da CPU para gerar dados aleatórios como entrada. Por exemplo, em Qual é a maneira mais rápida de gerar um arquivo de texto de 1 GB contendo dígitos aleatórios?lseek
que não funcionará.cat foo.mp4 | mpv -
funcionará, mas você não pode procurar mais além do que o buffer de cache do mpv ou mplayer. Mas com a entrada redirecionada de um arquivo, você pode.cat | mpv -
é uma maneira de verificar se um MP4 tem seumoov
átomo no início do arquivo, para que possa ser reproduzido sem procurar o final e o retorno (ou seja, se for adequado para transmissão). É fácil imaginar outros casos em que você deseja testar um programa para arquivos não procuráveis executando-o/dev/stdin
comcat
um redirecionamento.xargs cat | somecmd
. Se os caminhos do arquivo ultrapassarem o limite do buffer de comando,xargs
poderão ser executadoscat
várias vezes, resultando em um fluxo contínuo, enquanto o usoxargs somecmd
direto falhará frequentemente, porquesomecmd
não pode ser executado em múltiplos para obter um resultado contínuo.Porque detectar gatos inúteis é realmente muito difícil.
Eu tinha um script de shell onde escrevi
O script do shell falhou na produção se o
cat
foi removido porque foi chamado viasu -c 'script.sh' someuser
. O aparentemente supérfluocat
fez com que o proprietário da entrada padrão mudasse para o usuário em que o script estava sendo executado, de modo que a reabrisse por meio do/proc
trabalho.fonte
cat
seguido por exatamente um parâmetro, portanto o shell deve usarcat
executável real em vez de atalho otimizado. Um bom argumento sobre credenciais possivelmente diferentes ou padrões não padronizados para processos reais, no entanto.tl; dr: Os reservatórios não fazem isso automaticamente porque os custos excedem os benefícios prováveis.
Outras respostas apontaram a diferença técnica entre stdin ser um pipe e ser um arquivo. Tendo isso em mente, o shell pode fazer um dos seguintes:
cat
como um interno, ainda preservando a distinção entre arquivo e tubulação. Isso economizaria o custo de um executivo e talvez, possivelmente, de um garfo.Em seguida, você deve considerar os custos e benefícios de cada abordagem. Os benefícios são bastante simples:
cat
)Assim, você economiza um pouco de tempo e memória da CPU, especialmente se puder evitar o garfo. Obviamente, você só economiza esse tempo e memória quando o recurso é realmente usado. E você está realmente economizando o tempo do garfo / exec; com arquivos maiores, o tempo é principalmente o tempo de E / S (ou seja, gato lendo um arquivo do disco). Então, você deve perguntar: com que frequência é
cat
usado (inutilmente) em scripts de shell em que o desempenho realmente importa? Compare-o com outros componentes comuns do shell, comotest
- é difícil imaginar quecat
seja usado (inutilmente) até um décimo com a frequênciatest
usada em locais importantes. Esse é um palpite que ainda não medi, o que você gostaria de fazer antes de qualquer tentativa de implementação. (Ou da mesma forma, pedindo a outra pessoa para implementar, por exemplo, uma solicitação de recurso.)Em seguida, você pergunta: quais são os custos. Os dois custos que vêm à mente são: (a) código adicional no shell, que aumenta seu tamanho (e, portanto, possivelmente uso de memória), requer mais trabalho de manutenção, é outro ponto para erros, etc .; e (b) surpresas de compatibilidade com versões anteriores, o POSIX
cat
omite muitos recursos, por exemplo, GNU coreutilscat
, portanto, você deve ter cuidado exatamente com o que ocat
built-in implementaria.A opção embutida adicional provavelmente não é tão ruim - adicionando mais uma embutida onde um monte já existe. Se você tivesse dados de perfil mostrando a ajuda, provavelmente poderia convencer os autores do seu shell favorito a adicioná-los.
Quanto à análise do pipeline, acho que os projéteis não fazem nada parecido atualmente (alguns reconhecem o final de um pipeline e podem evitar um garfo). Essencialmente, você adicionaria um otimizador (primitivo) ao shell; os otimizadores geralmente se tornam códigos complicados e a fonte de muitos bugs. E esses erros podem ser surpreendentes - pequenas alterações no script do shell podem acabar evitando ou acionando o erro.
Postscript: Você pode aplicar uma análise semelhante aos usos inúteis do gato. Benefícios: mais fácil de ler (embora se command1 aceite um arquivo como argumento, provavelmente não). Custos: bifurcação e exec extra (e se o comando1 puder usar um arquivo como argumento, provavelmente mensagens de erro mais confusas). Se sua análise lhe disser para usar inutilmente o gato, vá em frente.
fonte
O
cat
comando pode aceitar-
como um marcador para stdin . ( POSIX , " Se um arquivo for '-', o utilitário cat deve ler a entrada padrão naquele ponto da sequência. ") Isso permite o manuseio simples de um arquivo ou stdin onde, caso contrário, isso não seria permitido.Considere estas duas alternativas triviais, onde o argumento do shell
$1
é-
:Outro momento
cat
útil é quando é usado intencionalmente como não operacional simplesmente para manter a sintaxe do shell:Finalmente, acredito que a única vez em que o UUOC pode realmente ser chamado corretamente é quando
cat
é usado com um nome de arquivo conhecido por ser um arquivo regular (ou seja, não é um dispositivo ou pipe nomeado) e que nenhum sinalizador é dado ao comando:Em qualquer outra situação, as
cat
próprias propriedades podem ser necessárias.fonte
O comando cat pode fazer coisas que o shell não pode necessariamente fazer (ou pelo menos, não pode fazer facilmente). Por exemplo, suponha que você queira imprimir caracteres que, de outra forma, seriam invisíveis, como guias, retornos de carro ou novas linhas. Pode haver uma maneira de fazer isso apenas com os comandos internos do shell, mas não consigo pensar em nada fora do topo da minha cabeça. A versão GNU do gato pode fazer isso com o
-A
argumento ou os-v -E -T
argumentos (embora eu não conheça outras versões do gato). Você também pode prefixar cada linha com um número de linha usando-n
(novamente, IDK se versões não-GNU puderem fazer isso).Outra vantagem do gato é que ele pode facilmente ler vários arquivos. Para fazer isso, pode-se simplesmente digitar
cat file1 file2 file3
. Para fazer o mesmo com um shell, as coisas ficariam complicadas, embora um loop cuidadosamente criado possa provavelmente alcançar o mesmo resultado. Dito isto, você realmente quer reservar um tempo para escrever esse loop, quando existe uma alternativa tão simples? Eu não!A leitura de arquivos com o gato provavelmente usaria menos CPU do que o shell, pois o cat é um programa pré-compilado (a exceção óbvia é qualquer shell que possua um gato interno). Ao ler um grande grupo de arquivos, isso pode se tornar aparente, mas nunca o fiz nas minhas máquinas, por isso não tenho certeza.
O comando cat também pode ser útil para forçar um comando a aceitar entrada padrão nas instâncias em que não pode. Considere o seguinte:
echo 8 | sleep
O número "8" não será aceito pelo comando "sleep", pois nunca foi realmente aceito para aceitar entrada padrão. Assim, o sono desconsiderará essa entrada, reclamará da falta de argumentos e sairá. No entanto, se alguém digitar:
echo 8 | sleep $(cat)
Muitas conchas expandirão isso para
sleep 8
, e o sono aguardará 8 segundos antes de sair. Você também pode fazer algo semelhante com o ssh:command | ssh 1.2.3.4 'cat >> example-file'
Este comando adiciona um arquivo de exemplo na máquina com o endereço 1.2.3.4 com o que for gerado a partir de "comando".
E isso é (provavelmente) apenas arranhando a superfície. Tenho certeza de que poderia encontrar mais exemplo de gato sendo útil se quisesse, mas este post é longo o suficiente. Então, concluirei dizendo o seguinte: pedir ao shell para antecipar todos esses cenários (e vários outros) não é realmente viável.
fonte
Lembre-se que um usuário pode ter um
cat
na sua$PATH
, que não é exatamente o POSIXcat
(mas talvez alguma variante que pode logar alguma coisa em algum lugar). Nesse caso, você não deseja que o shell o remova.o
PATH
poderia mudar dinamicamente, e entãocat
não é o que você acredita que é. Seria muito difícil escrever um shell fazendo a otimização que você sonha.Além disso, na prática,
cat
é um programa bastante rápido. Existem poucas razões práticas (exceto estética) para evitá-lo.Veja também o excelente Analisando POSIX [s] hell palestra sobre Parish Y de Yann Regis-Gianas no FOSDEM2018. Dá outras boas razões para evitar tentar fazer o que você sonha em um shell.
Se o desempenho fosse realmente um problema para os shells, alguém teria proposto um shell que usa otimização sofisticada do compilador de programa inteiro, análise estática de código-fonte e técnicas de compilação just-in-time (todos esses três domínios têm décadas de progresso e publicações científicas e dedicadas conferências, por exemplo, sob SIGPLAN ). Infelizmente, mesmo como um tópico de pesquisa interessante, que atualmente não é financiado por agências de pesquisa ou capitalistas de risco, e deduzo que simplesmente não vale a pena o esforço. Em outras palavras, provavelmente não há mercado significativo para a otimização de cascas . Se você tiver meio milhão de euros para gastar em tais pesquisas, encontrará facilmente alguém para fazer isso, e acredito que daria resultados valiosos.
Em termos práticos, reescrevendo, para melhorar seu desempenho, geralmente é feito um pequeno shell script (de cem linhas) em qualquer linguagem de script melhor (Python, AWK, Guile, ...). E não é razoável (por muitas razões de engenharia de software) escrever grandes scripts de shell: quando você está escrevendo um script de shell com mais de cem linhas, é necessário reescrevê-lo (mesmo por motivos de legibilidade e manutenção) em uma linguagem mais adequada : como linguagem de programação, o shell é muito ruim. No entanto, existem muitos scripts shell gerados grandes e por boas razões (por exemplo, GNU autoconf gerado
configure
scripts ).Em relação a grandes arquivos de texto, passá-los para
cat
um único argumento não é uma boa prática, e a maioria dos administradores de sistemas sabe que (quando qualquer script de shell leva mais de um minuto para ser executado, você começa a otimizá-lo). Para arquivos de gigabytes grandes, nuncacat
é a boa ferramenta para processá-los.fonte
cat some-huge-log | tail -n 5
correr (ondetail -n 5 some-huge-log
poderia pular direto até o fim, enquantocat
lê apenas de frente para trás) discordaria.cat
um arquivo de texto grande em dezenas de GB (que foi criado para teste) leva bastante tempo. Não recomendaria.Adicionando a resposta @Kusalananda (e comentário @alephzero), cat pode ser qualquer coisa:
ou
Não há razão para que cat (por si só) ou / usr / bin / cat no sistema seja realmente a ferramenta concatenada.
fonte
cat
é definido pelo POSIX e, portanto, não deve ser muito diferente.PATH=/home/Joshua/bin:$PATH cat ...
Tem certeza de que sabe o quecat
faz agora?cat
podemos ser substituídos, mas também sabemos que não deve ser substituído voluntariamente por outra coisa. Meu comentário aponta que o POSIX exige um determinado (subconjunto de) comportamento que pode ser razoavelmente esperado que exista. Às vezes, escrevi um script de shell que estende o comportamento de um utilitário padrão. Nesse caso, o script do shell agiu e se comportou exatamente como a ferramenta substituída, exceto pelo fato de possuir recursos adicionais./bin/cat
. (E você pode optar por desativar.) Ou criarcat
um shell interno (o que talvez recorra a/bin/cat
vários argumentos)? Para que os usuários possam controlar se desejam ou não a versão externa normal caminho, comenable cat
. Como parakill
. (Eu estava pensando que o bashcommand cat
iria funcionar, mas isso não pula os builtins)cat
nesse ambiente não se refere mais ao habitualcat
. Obviamente, a otimização deve ser implementada após o processamento dos aliases. Considero os shell embutidos para representar comandos no diretório virtual que sempre são anexados ao seu caminho. Se você deseja evitar a versão interna do shell de qualquer comando (por exemplotest
), é necessário usar uma variante com um caminho.Dois usos "inúteis" para o gato:
... aqui
cat
é usado para misturar entrada de arquivo e canalizada.... aqui, você
xargs
pode aceitar um número praticamente infinito de nomes de arquivos e executarcat
quantas vezes forem necessárias, fazendo com que tudo se comporte como um fluxo. Portanto, isso funciona para grandes listas de arquivos em que o uso diretoxargs sort
não.fonte
cat
for chamado com exatamente um argumento. Especialmente no caso em quesh
é passada uma string exargs
a chamada serácat
direta, não há como o shell usar sua implementação embutida.Além de outras coisas,
cat
-check adicionaria sobrecarga de desempenho adicional e confusão sobre qual usocat
é realmente inútil, IMHO, porque essas verificações podem ser ineficientes e criar problemas com legítimoscat
uso .Quando os comandos lidam com os fluxos padrão, eles precisam se preocupar apenas com a leitura / gravação nos descritores de arquivo padrão. Os comandos podem saber se stdin é procurável / isável ou não, o que indica um pipe ou arquivo.
Se adicionarmos à mistura a verificação de qual processo realmente fornece esse conteúdo stdin, precisaremos encontrar o processo do outro lado do canal e aplicar a otimização apropriada. Isso pode ser feito em termos de shell em si, como mostra a postagem de SuperUser de Kyle Jones, e em termos de shell que é
como mostrado na postagem vinculada. Esta é mais 3 comandos (modo extras
fork()
s eexec()
s) e percursos recursivos (por isso toda monte dereaddir()
chamadas).Em termos de código-fonte C e shell, o shell já conhece o processo filho, portanto não há necessidade de recursão, mas como sabemos quando otimizar e quando
cat
é realmente inútil? De fato, existem usos úteis do gato , comoProvavelmente seria desperdício e sobrecarga desnecessária adicionar essa otimização ao shell. Como a resposta de Kusalanda já mencionou, o UUOC é mais sobre a falta de entendimento do usuário sobre como combinar melhor os comandos para obter melhores resultados.
fonte