Portanto, isso é para trabalhos de casa, mas não farei a pergunta específica de trabalhos de casa.
Preciso usar cabeça e cauda para pegar diferentes conjuntos de linhas de um arquivo. Assim como as linhas 6-11 e 19-24 e salve-as em outro arquivo. Eu sei que posso fazer isso usando anexar como
head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.
Mas acho que não devemos.
Existe uma maneira específica de combinar os comandos head e tail e salvar no arquivo?
head
etail
? Nesse caso, sua solução é praticamente o melhor que você pode fazer. Se você tem permissão para usar outros programassed
ouawk
pode oferecer soluções melhores (ou seja, com menos invocações de processos).>>
), colocando os dois comandos em parênteses para redirecionar sua produção concatenadas:(head -11 file | tail -6; head -24 file | tail -6) > file1
. Realmente se resume à preferência pessoal, que é melhor.Respostas:
Você pode fazer isso com
head
aritmética sozinha e básica, se agrupar comandos{ ... ; }
usando uma construção comoonde todos os comandos compartilham a mesma entrada (obrigado @mikeserv ).
Obter as linhas 6-11 e 19-24 é equivalente a:
Então, basicamente, você executaria:
fonte
Você pode usar a
{ … }
construção de agrupamento para aplicar o operador de redirecionamento a um comando composto.Em vez de duplicar as primeiras linhas M + N e manter apenas o último N, você pode pular as primeiras linhas M e duplicar o próximo N. Isso é notavelmente mais rápido em arquivos grandes . Observe que o
+N
argumento detail
não é o número de linhas a serem ignoradas, mas uma mais a que é - é o número da primeira linha a ser impressa com linhas numeradas de 1.De qualquer forma, o arquivo de saída é aberto apenas uma vez, mas o arquivo de entrada é percorrido uma vez para cada fragmento extraído. Que tal agrupar as entradas?
Em geral, isso não funciona. (Pode funcionar em alguns sistemas, pelo menos quando a entrada é um arquivo comum.) Por que? Por causa do buffer de entrada . A maioria dos programas, inclusive
tail
, não lê sua entrada byte a byte, mas alguns kilobytes por vez, porque é mais rápido. Entãotail
lê alguns kilobytes, pula um pouco no começo, passa um pouco mais parahead
e para - mas o que é lido é lido e não está disponível para o próximo comando.Outra abordagem é usar
head
canalizado/dev/null
para pular linhas.Novamente, isso não garante que funcione, devido ao armazenamento em buffer. Por acaso, funciona com o
head
comando do GNU coreutils (o encontrado em sistemas Linux não incorporados), quando a entrada é de um arquivo comum. Isso porque uma vez que essa implementaçãohead
leu o que deseja, ela define a posição do arquivo para o primeiro byte que não foi gerado. Isso não funciona se a entrada for um pipe.Uma maneira mais simples de imprimir várias seqüências de linhas de um arquivo é chamar uma ferramenta mais generalista, como sed ou awk . (Isso pode ser mais lento, mas importa apenas para arquivos extremamente grandes.)
fonte
Sei que você disse que precisa usar cabeça e cauda, mas sed é definitivamente a ferramenta mais simples para o trabalho aqui.
Você pode até construir os blocos em uma string com outro processo e executá-lo através do sed.
-n nega a saída e especifica os intervalos a serem impressos com p, com o primeiro e o último número do intervalo separados por vírgula.
Dito isto, você pode executar o agrupamento de comandos sugerido pelo @don_crissti ou percorrer o arquivo algumas vezes com a cabeça / cauda pegando um pedaço de linhas toda vez que passar.
Quanto mais linhas em um arquivo e mais blocos você tiver, mais sed será eficiente.
fonte
Com
sed
você pode fazer:... Possivelmente uma solução mais eficiente poderia ser obtida
head
. Don já demonstrou como isso pode funcionar muito bem, mas eu também tenho brincado com isso. Algo que você pode fazer para lidar com este caso específico:... que chamaria
head
quatro vezes escrevendo paraoutfile
ou para,/dev/null
dependendo se o valor dessa iteração$n
é um número par ou ímpar.Para casos mais gerais, juntei isso de outras coisas que já tinha:
Isso pode ser feito da seguinte maneira:
... que imprime ...
Ele espera que seu primeiro argumento seja uma contagem de repetição prefixada com a
-
ou, na sua falta, apenas a-
. Se uma contagem for fornecida, ela repetirá o padrão de linha fornecido nos argumentos a seguir tantas vezes quanto especificado e parará assim que isso for feito.Para cada argumento a seguir, ele interpretará um número inteiro negativo para indicar uma contagem de linhas que deve ser gravada
/dev/null
e um número inteiro positivo para indicar uma contagem de linhas que deve ser gravadastdout
.Portanto, no exemplo acima, ele imprime as primeiras 5 linhas para
/dev/null
, as próximas 6 parastdout
, as próximas 7 para/dev/null
novamente e as próximas 6 novamente parastdout
. Depois de atingir o último de seus argumentos e alternar completamente a-1
contagem de repetições, ele sai. Se o primeiro argumento tivesse sido,-2
ele repetiria o processo mais uma vez, ou-
pelo maior tempo possível.Para cada ciclo de arg, o
while
loop é processado uma vez. No topo de cada loop, a primeira linha destdin
é lida na variável shell$l
. Isso é necessário porquewhile head </dev/null; do :; done
se repetirá indefinidamente -head
indica em seu retorno quando ele atinge o final do arquivo. Portanto, a verificação no EOF é dedicadaread
eprintf
escreverá$l
mais uma nova linhastdout
apenas se o segundo argumento for um número inteiro positivo.A
read
verificação complica um pouco o loop, porque imediatamente após outro loop ser chamado - umfor
loop que itera sobre args,2-$#
conforme representado em$n
cada iteração dowhile
loop pai . Isso significa que, para cada iteração, o primeiro argumento deve ser decrementado em um do valor especificado na linha de comando, mas todos os outros devem manter seus valores originais e, portanto, o valor do$_n
marcador var é subtraído de cada um, mas apenas mantém um valor valor maior que 0 para o primeiro argumento.Isso constitui o loop principal da função, mas a maior parte do código está na parte superior e destina-se a permitir que a função armazene de maneira limpa até mesmo um pipe como entrada. Isso funciona chamando primeiro um background
dd
para copiar seu arquivo tmpfile na saída em tamanhos de bloco de 4k por peça. A função então configura um loop de espera - que quase nunca deve completar nem um único ciclo completo - apenas para garantir quedd
tenha feito pelo menos uma única gravação no arquivo antes que a função substitua seu stdin por um descritor de arquivo vinculado ao tmpfile e depois, desconecta imediatamente o arquivo comrm
. Isso permite que a função processe o fluxo de maneira confiável, sem a necessidade de interrupções ou limpeza - assim que a função liberar sua reivindicação no arquivo fd, o tmpfile deixará de existir porque seu único link do sistema de arquivos nomeado já foi removido.fonte
Use uma função bash como esta:
Isso é um exagero nesse caso, mas se seus filtros aumentarem, pode se tornar um benefício.
fonte