Listar (ou mover) apenas arquivos com um determinado número de linhas?

2

Lidando com um monte de arquivos de configuração de duas linhas, eu gostaria de uma maneira de excluir quaisquer arquivos que tenham um número diferente de linhas.

Então, algo como:

mv * destdir only if file contains exactly two lines

Ou:

wc -l * | grep '^ *2' | xargs mv {} destdir

Só que nenhum deles é um código de trabalho real.

Enquanto escrevia isso, percebi que eu faço ter uma maneira de fazer isso, o que é feio como diabos, e eu incluí-lo abaixo como uma resposta.

Existe uma maneira fácil / limpa de fazer isso?

    
por Wildcard 26.11.2015 / 02:13

5 respostas

3

Sua solução kludgy não é tão ruim para começar ... você está perdendo o fato de que não só pode awk dar-lhe o número de linhas, você também pode instruí-lo para exit com o código de status correto para que você possa encadeá-lo com o comando cp :

for file in * ; do awk 'NR==3{exit}END{exit NR!=2}' "$file" && cp "$file" /tmp; done

NR é o número de registros , e como sugerido na resposta do @don_crissti , podemos usar a NR==3 check para parar o processamento adicional uma vez encontramos uma terceira linha.

NR!=2 parece engraçado, porque os valores de awk do true/false são 1/0 , mas no shell, precisamos que 0 represente um status de sucesso para que && funcione corretamente. O inverso disso também funciona (dependendo de quão strongmente você reage a ver != ):

for file in * ; do awk 'NR==3{exit}END{exit NR==2}' "$file" || cp "$file" /tmp; done
    
por 26.11.2015 / 07:51
3

Você pode usar awk , exit na linha 3 (a regra END ainda é executada) e exit 1 no bloco END se não. de linhas não é 2 , e. com zsh :

print -rl -- *(.e_'awk "NR==3{exit}END{if(NR!=2){exit 1}}" $REPLY'_)

listará arquivos de duas linhas no diretório atual; substitua print -rl por mv e adicione o destino se quiser movê-los.
Com outras conchas:

for file in ./*; do [[ -f $file ]] && \
awk 'NR==3{exit};END{if(NR!=2){exit 1}}' "$file" && mv "$file" "$dest"
done

Outras formas, por ex. com z shell
e gnu awk :

awk 'ENDFILE{if(FNR==2){print FILENAME}}' ./*(.)

ou gnu sed (v. 4.2.2 ou posterior):

sed -ns '2{$F}' ./*(.)

para listar os arquivos de duas linhas 1 e, por exemplo:

for f (./*(.))
sed -n '2{$Q 1};3q' $f || mv $f $dest

para movê-los.

1: os dois passam por toda a entrada, então não são adequados se você estiver trabalhando com arquivos grandes; Nesse caso, você pode querer sed -n '2{$F};3q' para cada arquivo ou usar a primeira solução awk

    
por 26.11.2015 / 03:44
2

se os nomes dos seus arquivos forem razoavelmente saudáveis e você puder delimitar tanto : quanto uma nova linha, então:

grep -m3 '' ./* ./*/* |
cut -d: -f1 | uniq -c |
grep -v '^ *[13] ' 

^ esse comando listará todos os arquivos não pontuais no diretório atual e em todos os diretórios filhos imediatos que contêm apenas duas linhas.

Você não precisa se preocupar com a classificação de uniq , porque os globs são classificados. Eu uso a opção de correspondência -m ax do GNU porque é muito mais rápido se grep fechar na terceira linha de entrada do que se continuar até o final, mas funcionará sem ela também. A idéia é obter grep para imprimir os nomes dos arquivos para cada linha que eles contêm, então contar as ocorrências de cada nome de arquivo em sua saída e, em seguida, filtrar qualquer coisa maior ou menor que 2.

Eu corri contra alguns diretórios de código-fonte aleatórios, e, de todos eles, eu tinha dois arquivos que continham apenas as duas linhas:

  2 ./dex/coll.sh
  2 ./jimtcl/jim-config.h.in

seria melhor substituir a última linha por:

... |
sed -ne's/^ *2  *//p'

... embora.

    
por 26.11.2015 / 05:04
0

Eu trabalhei com a seguinte solução "kludgy":

for file in * ; do if [ "$(wc -l "$file" | awk '{print $1}')" == "2" ] ; then cp "$file" /tmp/ ; fi; done

Deve haver uma maneira melhor de não iniciar dois processos para cada arquivo no diretório atual.

    
por 26.11.2015 / 02:13
0

Usando a opção de diretório -t target em mv com xargs :

wc -l * | sed -n 's/^[[:space:]]*2[[:space:]]\+//p'  | xargs mv -t "$DESTDIR"
    
por 26.11.2015 / 02:57