O que é uma maneira rápida e não muito complicada de excluir todos os arquivos em um diretório com menos de x linhas, no bash?
Aqui está uma solução POSIX que deve ser bastante simples de entender:
find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;
Como na resposta de Stephane , remova o echo
quando estiver satisfeito com o que será removido.
O ponto .
representa o diretório atual. find
localiza arquivos e diretórios recursivamente dentro .
e pode fazer coisas com eles.
-type
é um dos find
's primárias ; é um teste que será realizado para cada arquivo e diretório que é recursivamente encontrado (dentro .
), eo resto das primárias na linha só são avaliadas Se isto resultar em "verdade".
Nesse caso em particular, só continuamos se estivermos lidando com um arquivo regular , não com um diretório ou outra coisa (por exemplo, um dispositivo de bloco).
O -exec
primário (de find
) chama um comando externo e só prossegue para o próximo primário se o comando externo sair com êxito (status de saída de "0"). O {}
é substituído pelo nome do arquivo "considerado" pelo find
comando. Portanto, a primeira -exec
chamada é equivalente ao seguinte comando shell, executado para cada arquivo por sua vez:
awk -v x=10 'NR==x{exit 1}' ./somefilename
O Awk é um idioma inteiro, desenvolvido para lidar com arquivos de texto delimitados, como CSVs. Os condicionais e comandos do Awk (que estão contidos entre aspas simples e iniciam com as letras NR
) são executados para cada linha de um arquivo de texto. (Loop implícito.)
Para aprender o Awk completamente, recomendo o Tutorial Grymoire , mas explicarei os recursos do Awk usados no comando acima.
O -v
sinalizador para Awk permite definir uma variável do Awk (uma vez) antes que os comandos do Awk sejam executados (para cada linha do arquivo). Nesse caso, configuramos x
para 10
.
NR
é uma variável especial Awk referindo-se à " N umber da corrente R ecord." Em outras palavras, é o número da linha que estamos vendo em qualquer passagem específica do loop.
(Note que é possível, embora incomum, para usar um "diferente R ecord S eparator" do que o padrão de um caractere de nova linha, por definição RS
. Aqui está um exemplo de jogar com separadores de registro. )
Os scripts awk geralmente consistem em condições (chaves externas) combinadas com ações (chaves internas). Pode haver condições e ações compostas, e há uma condição padrão (true) e uma ação padrão (print), mas precisamos não se preocupe com isso.
A condição aqui é: "Essa é a décima linha?" Se for esse o caso, saímos com um status de saída diferente de zero, que em scripts de shell significa "finalização malsucedida do comando".
Portanto, a única maneira de este comando Awk sair com êxito é se o final do arquivo for alcançado antes da 10ª linha.
Portanto, se o script Awk sair com êxito, significa que você tem um arquivo com menos de dez linhas.
A próxima -exec
chamada (se você remover o echo
) removerá cada arquivo (que chega tão longe na avaliação das find
primárias) executando:
rm -f ./somefilename
Supondo que uma find
implementação seja compatível com o -readable
predicado (se o seu find
não for compatível, remova-o, você receberá mensagens de erro para arquivos não legíveis ou substitua por -exec test -r {} \;
):
x=10 find . -type f -readable -exec sh -c '
for file do
lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
done' sh {} +
Remova o echo
se estiver feliz.
Isso não é particularmente eficiente em que conta todas as linhas em cada arquivo enquanto ele só precisa parar no x
th um e ele corre um wc
(e potencialmente um rm
) comando para cada arquivo.
Com o GNU awk
, você pode torná-lo muito mais eficiente com:
x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
FNR == x {nextfile}
ENDFILE {if (FNR < x) print FILENAME}' {} +|
xargs -r0 echo rm -f
(novamente, remova echo
quando estiver feliz).
O mesmo com perl
:
x=10 find . -type f -readable -exec perl -Tlne '
if ($. == $ENV{x}) {close ARGV}
elsif (eof) {print $ARGV; close ARGV}' {} +
Substitua print
por unlink
se estiver feliz.
sh
? 2. Éwc -l < "$file"
mais rápido quewc -l "$file"
? 3. Como sh sabe o valor de$x
, definido no shell Bash de chamada?sh
é o que existe nesse script embutido$0
, para ser usado para mensagens de erro, por exemplo.wc -l "$file"
imprimiria o nome do arquivo que não queremos aqui e seria executadowc
mesmo que o arquivo não possa ser aberto.$x
é exportado parafind
(x=10 find...
) que por si só o passash
.find: -readable: unknown primary or operator
.bash
.bash
é apenas um intérprete de linha de comando, mas dafind
implementação.-readable
é uma extensão GNU, não está disponível no OS / Xfind
. É usado apenas para limitar os arquivos que são legíveis (você não seria capaz de obter a contagem de linhas para arquivos não legíveis). Você pode omiti-lo para o primeiro, então você receberá apenas mensagens de erro ao abrir os arquivoswc
para os que não são legíveis.Por uma questão de integridade, além do AWK, você também pode usar o GNU sed para obter o mesmo resultado:
O que resulta em uma linha de comando um pouco mais concisa.
Explicação
fonte