Como ordenar os arquivos no Bash com os timestamps ddmmyy no nome

2

Eu tenho uma série de arquivos com nomes no formato "foo.date.bar", em que "date" é um campo de seis dígitos, como 310715.

Então, por exemplo

foo.310715.bar
foo.260815.bar
foo.110815.bar
foo.040815.bar

Eu gostaria de classificá-los em ordem de data, com base na data no nome do arquivo e não em nenhum metadado de arquivo, para que meu script possa excluir alguns deles. Normalmente eu faria isso em Python ou PHP, onde seria fácil, mas estou tentando aprender a fazer as coisas no Bash. Eu fiz uma primeira tentativa com o comando

for f in $( find $dir -type f | sort -r -t. -k 2 ); do
    echo $f
done

mas depois percebi que classificar a segunda coluna numericamente ou em ordem alfabética não tem utilidade, tenho que classificá-la como uma data. Não parece ser possível dizer a sort como tratar o campo de seis dígitos como uma data ou como tratá-lo como três colunas de dois dígitos. Gostaria de saber se o próximo passo seria usar sed ou tr ou algo parecido para transformar o campo de seis dígitos em algo que sort possa analisar?

Agradecemos antecipadamente por qualquer ajuda,

MB

Obrigado a todos por suas excelentes respostas, aprendi muito lendo-os.

    
por Monkeybrain 31.03.2016 / 19:09

3 respostas

1

Aqui está um abuso de matrizes bash; ele separa o registro de data e hora e cria entradas de matriz com base na ordem YYMMDD e, em seguida, imprime a matriz novamente em ordem.

declare -a array
for file in foo.*.bar
do
  [[ $file =~ foo.([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2}).bar ]] && \
    {
      index="${BASH_REMATCH[3]}${BASH_REMATCH[2]}${BASH_REMATCH[1]}"
      array[$index]="$file"
    }
done

for index in "${array[@]}"
do
  echo $index
done

# or
printf "%s\n" ${array[@]}
    
por 31.03.2016 / 19:32
0

A seguinte sequência canalizada utiliza sed para alterar primeiro os nomes dos ficheiros que estão no formato *.DDMMYY.* para o formato *|DD|MM|YY|* . A saída reformatada é canalizada para sort , em que o '|' é usado como o separador de campo e classificado primeiro por YY ( -k4n ), depois por MM ( -k3n ) e, finalmente, por DD ( -k2n ). Em seguida, a saída classificada é canalizada de volta para sed , onde o nome do arquivo é transformado de volta para o formato original *.DDMMYY.* .

sed 's/\.\([[:digit:]]\{2\}\)\([[:digit:]]\{2\}\)\([[:digit:]]\{2\}\)\./||||/' | \
sort -t'|' -k4n -k3n -k2n | \
sed 's/|\([[:digit:]]\{2\}\)|\([[:digit:]]\{2\}\)|\([[:digit:]]\{2\}\)|/../'

Usando o seguinte exemplo de arquivos:

$ ls *bar -1
abc.291015.bar
abc.291115.bar
abc.291215.bar
abc.301215.bar
foo.040815.bar
foo.150115.bar
foo.150914.bar
foo.260815.bar
foo.301216.bar
foo.310715.bar
xyz.010113.bar

A sequência produzirá o seguinte:

xyz.010113.bar
foo.150914.bar
foo.150115.bar
foo.310715.bar
foo.040815.bar
foo.260815.bar
abc.291015.bar
abc.291115.bar
abc.291215.bar
abc.301215.bar
foo.301216.bar
    
por 31.03.2016 / 20:28
0

Se você tem o GNU ou FreeBSD sort , você pode usar a opção -V ou --version-sort , depois de usar sed para trocar o formato da data (e sed novamente para alterar o formato da data ):

ls -1 | 
    sed -E -e 's/^(.*\.)(..)(..)(..)(.*)$//' | 
    sort -V | 
    sed -E -e 's/^(.*\.)(..)(..)(..)(.*)$//'

Idealmente, você deve apenas renomear os arquivos para que eles tenham um formato de data útil. por exemplo. usando o utilitário de renomeação perl prename :

$ prename -v 's/^(.*\.)(..)(..)(..)(.*)$/$1$4$3$2$5/' *
foo.040815.bar renamed as foo.150804.bar
foo.110815.bar renamed as foo.150811.bar
foo.260815.bar renamed as foo.150826.bar
foo.310715.bar renamed as foo.150731.bar
$ ls -1 | sort -V
foo.150731.bar
foo.150804.bar
foo.150811.bar
foo.150826.bar

(BTW, diferente da maioria das operações de prename , este é reversível. Se você precisar, você pode simplesmente executá-lo novamente para renomeá-los para o que eram)

    
por 01.04.2016 / 02:03