Comando shell do Linux para filtrar um arquivo de texto pelo comprimento da linha

19

Eu tenho uma imagem de disco de 30 GB de uma partição borked (acho dd if=/dev/sda1 of=diskimage) da qual preciso recuperar alguns arquivos de texto. As ferramentas de gravação de dados foremostsó funcionam em arquivos com cabeçalhos bem definidos, ou seja, não em arquivos de texto sem formatação, então voltei ao meu bom amigo strings.

strings diskimage > diskstrings.txt produzi um arquivo de texto de 3GB contendo um monte de strings, principalmente coisas inúteis, misturadas com o texto que eu realmente quero.

A maior parte da crosta tende a ser realmente longas e sem interrupções. O material em que estou interessado tem menos de 16kb, portanto, filtrarei o arquivo pelo comprimento da linha. Aqui está o script Python que estou usando para fazer isso:

infile  = open ("infile.txt" ,"r");
outfile = open ("outfile.txt","w");
for line in infile:
    if len(line) < 16384:
        outfile.write(line)
infile.close()
outfile.close()

Isso funciona, mas para referência futura: Há alguma encantamentos mágica de uma linha (pense awk, sed) que filtrar um arquivo por comprimento de linha?

Li-aung Yip
fonte

Respostas:

28
awk '{ if (length($0) < 16384) print }' yourfile >your_output_file.txt

imprimiria linhas menores que 16 kilobytes, como no seu próprio exemplo.

Ou se você gosta de Perl:

perl -nle 'if (length($_) < 16384) { print }' yourfile >your_output_file.txt
Janne Pikkarainen
fonte
Bem, isso foi embaraçosamente simples. Obrigado. :)
Li-aung Yip
Adicionada também a versão Perl :-)
Janne Pikkarainen
E o script awk pode ser escrito como awk 'length($0) < 16384' file > output, pois a ação padrão é imprimir a linha.
Glenn Jackman
8

Isso é semelhante à resposta de Ansgar, mas um pouco mais rápido nos meus testes:

awk 'length($0) < 16384' infile >outfile

É a mesma velocidade que as outras respostas do awk. Ela se baseia no implícito printde uma expressão verdadeira, mas não precisa de tempo para dividir a linha como a de Ansgar.

Observe que o AWK oferece um ifde graça. O comando acima é equivalente a:

awk 'length($0) < 16384 {print}' infile >outfile

Não há explícito if(ou seu conjunto circundante de chaves) como em algumas das outras respostas.

Aqui está uma maneira de fazer isso sed:

sed '/.\{16384\}/d' infile >outfile

ou:

sed -r '/.{16384}/d' infile >outfile

que exclui qualquer linha que contenha 16384 (ou mais) caracteres.

Para garantir a integridade, eis como você usaria sedpara salvar linhas maiores que o seu limite:

sed '/^.\{0,16383\}$/d' infile >outfile
Pausado até novo aviso.
fonte
2

Você pode awk:

$ awk '{ if (length($0) < 16384) { print } }' /path/to/text/file

Isso imprimirá as linhas com comprimento menor que 16K caracteres (16 * 1024).

Você greptambém pode usar :

$ grep ".\{,16384\}" /path/to/text/file

Isso imprimirá as linhas com no máximo 16K caracteres.

Khaled
fonte
Não grepé uma boa idéia - é uma regexp simples, com certeza, mas mais computacionalmente cara do que awk. "Um homem com problemas diz:" Vou usar expressões regulares! Agora ele tem dois problemas. " ;)
Li-aung Yip
É apenas outra maneira de fazê-lo. A primeira opção que publiquei estava usando awk.
Khaled
1
+1 para o regexp, porque ele joga melhor e não me faz ler páginas de manual desa = =)
Ciro Santilli
2

Não é realmente diferente das respostas já dadas, mas é mais curto ainda:

awk -F '' 'NF < 16384' infile >outfile
Ansgar Esztermann
fonte