Obtendo um erro de saída com a cabeça de comando no shell script

1

recebendo um erro com o comando head. Existem dois arquivos com tamanho de 48 bytes no diretório e sua impressão uma vez junto com o caminho do diretório em vez de duas vezes. Por que o comando head não está considerando os dois primeiros arquivos, por exemplo, head -n 2 ? Existe algum outro método para fazer?

Meu código:

find "$dir" -type f -printf '%s %p\n' | sort -n -r | head -n 2|
             {
              read -r file dir
              printf "size: %d\n\t%s\n" "$file" "$dir"
              }

Meu erro de saída:

size: 48
      testdir/file7.txt

O diretório testdir contém dois arquivos com o mesmo tamanho 48, mas lançando apenas uma vez junto com o caminho do diretório, em vez de duas vezes

Minha saída desejada:

size: 48
      testdir/file7.txt
      testdir/file1.txt
    
por buddha sreekanth 20.05.2015 / 13:05

1 resposta

3

Não há erro com head -n 2 ; Você pode verificar isso removendo o | e o código subseqüente.

O problema é que o código entre as chaves só é executado uma vez - não é um loop. E read só lê dados de uma única linha de entrada. Então você precisa fazer algum tipo de loop para imprimir dados para vários arquivos.

Você pode usar um loop while ou aproveitar o loop integrado do awk para ler & imprima os dados. Por exemplo, o comando awk abaixo apenas imprime as informações de tamanho se o tamanho do arquivo atual for diferente do tamanho do arquivo anterior.

awk 'BEGIN{size=-1}; {if($1!=size){size=$1; printf "size: %d\n", size}; printf "\t%s\n", $2}'

Não precisamos realmente inicializar explicitamente size , pois ele é inicializado automaticamente para a sequência vazia, mas é bom ser explícito sobre essas coisas, IMHO.

Esse comando do awk substitui o

{
    read -r file dir
    printf "size: %d\n\t%s\n" "$file" "$dir"
}

seção do seu código. Em outras palavras, você pode usar

find "$dir" -type f -printf '%s %p\n' |
sort -n -r | head -n 2 |
awk 'BEGIN{size=-1}; 
{if($1!=size){size=$1; printf "size: %d\n", size}; 
printf "\t%s\n", $2}'

Você pode colocar tudo em uma linha ou dividi-lo em várias linhas. Também é possível colocar o programa awk em seu próprio arquivo, mas não há necessidade de fazer isso para um programa tão pequeno.

Observe que você pode tornar a opção -n em head tão grande quanto desejar e o programa awk se comportará como esperado. Observe também que o awk é muito rápido - é muito mais eficiente do que usar read e printf .

FWIW, código awk para processamento de texto simples é frequentemente mais rápido que código Python equivalente, então mesmo que muitos considerem o awk antiquado ainda é bastante popular.

Para imprimir os dados para somente o (s) maior (s) arquivo (s) em um diretório, você pode fazer isso:

find . -type f -printf '%s %p\n' | 
sort -nr | 
awk 'NR==1{size=$1;printf "size: %d\n", size};
$1!=size{exit};
{printf "\t%s\n", $2}'

O NR==1 diz para executar o seguinte bloco (o material dentro do {} ) somente quando o Número do Registro for igual a 1 - um registro é apenas uma linha. Assim, obtemos o tamanho do primeiro arquivo, que é o maior arquivo (graças ao comando sort anterior), salve-o na variável size e imprima o tamanho.

$1!=size{exit} diz para sair do programa assim que lermos uma linha em que os dados no primeiro campo não correspondem ao que salvamos na variável size .

O último bloco {printf "\t%s\n", $2} imprime o nome do caminho de cada arquivo.

Existem várias maneiras de imprimir os arquivos maiores e menores encontrados pelo comando find . Uma maneira seria ler todos os dados no awk, armazená-los em uma matriz, classificar a matriz e, em seguida, imprimir os dados para os arquivos de no máximo & tamanho mínimo. Mas vou adotar uma estratégia mais simples aqui e reciclar meu código existente. Para fazer isso de maneira mais eficiente, colocarei o programa awk em um arquivo. Salve este arquivo em um diretório em seu comando PATH e conceda a ele permissão de execução.

field1match.awk

#!/usr/bin/awk -f

# print only the records whose 1st field matches that of the 1st record
# Written by PM 2Ring 2015.05.21

NR==1{size=$1; printf "size: %d\n", size}
$1!=size{exit}
{printf "\t%s\n", $2}

E aqui está a linha de comando que usa tee para duplicar a saída de find e, em seguida, classificá-la e imprimi-la usando a substituição de processo:

find "$dir" -type f -printf '%s %p\n' | 
tee > >(sort -n | field1match.awk) >(sort -rn | field1match.awk)
    
por 20.05.2015 / 13:34