Renomeando um monte de arquivos com data e hora modificadas no final do nome do arquivo?

14

Então, digamos, eu tenho um diretório com vários arquivos como g.txt, em que o g.txt foi modificado pela última vez em, por exemplo, 20 de junho de 2012.

Como renomear em lote todos os arquivos (como g.txt) com a data da última modificação de 20 de junho de 2012 anexada ao final?

InquilineKea
fonte

Respostas:

13

Bash one-liner rápido e sujo para renomear todos os arquivos (globbed) no diretório atual de filename.txtpara filename.txt-20120620:

for f in *; do mv -- "$f" "$f-$(stat -c %Y "$f" | date +%Y%m%d)"; done

Um nerd empreendedor do Bash encontrará um caso de vantagem para romper, tenho certeza. :)

Obviamente, isso não faz coisas desejáveis, como verificar se um arquivo já possui algo parecido com uma data no final.

jgoldschrafe
fonte
2
+1, parece ser bom. você pode torná-lo um pouco mais robusto usando '-' como o primeiro argumento para mv. guarda contra filenaes começando com '-'
cas
5
Que tipo de dateimplementação é essa? Meu GNU datenão parece lidar com entrada.
manatwork
coreutils 8.4 no RHEL 6.
jgoldschrafe
2
@jgoldschrafe, você tem certeza de que o código funciona corretamente para você? pastebin.com/nNYubf3A
manatwork
5
boa captura de manatwork. minha data 8.13 do GNU também não suporta a leitura do stdin. Ele suporta o uso da hora da modificação de um arquivo com "-r" - por exemplo, "data +% Y% m% d -r $ f", portanto, o stat nem é necessário nesse caso específico.
cas
18

aqui está uma versão do one-liner de goldschrafe que:

  • não usa stat
  • trabalha com versões anteriores da data GNU
  • lida corretamente com qualquer espaço nos nomes de arquivos
  • também lida com nomes de arquivos começando com um hífen

    for f in *; do mv -- "$f" "$f-$(date -r "$f" +%Y%m%d)"; done

cas
fonte
1
Você deve fazê-lo date -r "$f" +%Y%m%dou não funcionará se POSIXLY_CORRECTestiver no ambiente. Geralmente, as opções devem ir antes de outros argumentos.
Stéphane Chazelas
esta deve ser a "resposta correta"
invalidusername
Claro que você não precisa do aparelho "${f}".
G-Man diz 'Reinstate Monica'
mas como você faria isso de modo que as datas precedidas por exemplo, "Ymd-filename"?
timebandit
2

Uma linha obrigatória do zsh (sem contar o carregamento único de componentes opcionais):

zmodload zsh/stat
autoload -U zmv
zmv -n '(*)' '$1-$(stat -F %Y%m%d +mtime -- $1)'

Usamos o statbuilt-in do zsh/statmódulo e a zmvfunção para renomear arquivos. E aqui está um extra que coloca a data antes da extensão, se houver.

zmv -n '(*)' '$1:r-$(stat -F %Y%m%d +mtime -- $1)${${1:e}:+.$1:e}'
Gilles 'SO- parar de ser mau'
fonte
1

Pelo que entendi, não sabemos de antemão qual é a data da modificação. Portanto, precisamos obtê-lo de cada arquivo, formatar a saída e renomear cada arquivo de forma que inclua a data da modificação nos nomes dos arquivos.

Você pode salvar esse script como algo como "modif_date.sh" e torná-lo executável. Nós o chamamos com o diretório de destino como argumento:

modif_date.sh txt_collection

Onde "txt_collection" é o nome do diretório em que temos todos os arquivos que queremos renomear.

#!/bin/sh

# Override any locale setting to get known month names
export LC_ALL=c
# First we check for the argument
if [ -z "$1" ]; then
    echo "Usage: $0 directory"
    exit 1
fi

# Here we check if the argument is an absolute or relative path. It works both ways
case "${1}" in
  /*) work_dir=${1};;
  *) work_dir=${PWD}/${1};;
esac

# We need a for loop to treat file by file inside our target directory
for i in *; do
    # If the modification date is in the same year, "ls -l" shows us the timestamp.
    # So in this case we use our current year. 
    test_year=`ls -Ggl "${work_dir}/${i}" | awk '{ print $6 }'`
    case ${test_year} in *:*) 
        modif_year=`date '+%Y'`
        ;;
    *)
        modif_year=${test_year}
        ;;
    esac
    # The month output from "ls -l" is in short names. We convert it to numbers.
    name_month=`ls -Ggl "${work_dir}/${i}" | awk '{ print $4 }'`
    case ${name_month} in
            Jan) num_month=01 ;;
            Feb) num_month=02 ;;
        Mar) num_month=03 ;;
        Apr) num_month=04 ;;
        May) num_month=05 ;;
        Jun) num_month=06 ;;
        Jul) num_month=07 ;;
        Aug) num_month=08 ;;
        Sep) num_month=09 ;;
        Oct) num_month=10 ;;
        Nov) num_month=11 ;;
        Dec) num_month=12 ;;
        *) echo "ERROR!"; exit 1 ;;
    esac
    # Here is the date we will use for each file
    modif_date=`ls -Ggl "${work_dir}/${i}" | awk '{ print $5 }'`${num_month}${modif_year}
    # And finally, here we actually rename each file to include
    # the last modification date as part of the filename.
    mv "${work_dir}/${i}" "${work_dir}/${i}-${modif_date}"
done
Aline
fonte
1
leia a página de manual do stat (1) para descobrir como você pode reduzir seu script em cerca de 3/4 ... a maioria dos itens acima é reinventar a roda. em particular, 'stat -c% Y' fornecerá o tempo de modificação de um arquivo em segundos desde a época (01-01-2009 00:00:00). Isso pode ser usado como entrada para a data (1) para formatar o registro de data e hora, conforme necessário. veja a resposta de jgoldschrafe acima para um exemplo.
cas
@CraigSanders A vantagem é que esse script funciona em sistemas operacionais diferentes do Linux, que possuem um statutilitário diferente ou nenhum.
Gilles 'SO- stop be evil'
Observe que a análise da saída de lsnão é confiável . Em algumas variantes do unix, os nomes de usuários e grupos podem conter espaços, o que prejudicará o alinhamento das colunas da data.
Gilles 'SO- stop be evil'
@ giles: true, mas qualquer pessoa sensata :-) instalaria o GNU coreutils (e todas as outras ferramentas GNU) em qualquer sistema não-linux. É a maneira mais fácil de obter não apenas um bom conjunto de ferramentas da terra do usuário, mas também um conjunto consistente, independentemente do kernel subjacente.
19412
1
(Continua)… (5) Outro bug: assume que qualquer data / hora exibida ls como data e hora (em vez de data e ano) deve estar no ano atual. Isso não é verdade. É nos últimos seis meses; isso pode ocorrer no ano anterior (por exemplo, 27 de setembro de 2016 a 31 de dezembro de 2016 estão nos últimos seis meses). (6) Com o risco de imitar o Wiki de Greg (que o @Gilles já citou), os nomes de arquivos que contêm nova linha (s) e espaços podem causar falhas. ... (continua)
G-Man diz 'Restabelecer Monica'
0

Aqui está a versão do one-liner do cas, (baseado no oneliner da goldschrafe) estendida com idempotência ,

ou seja, estendido para arquivos de prefixo com data e hora e para fazer isso apenas para aqueles que ainda não possuem prefixo de data e hora .

Caso de uso: útil se você incluir novos arquivos no diretório e desejar adicionar o prefixo de data e hora para aqueles que ainda não possuem um.

for f in * ; do
  if [[ "$f" != ????-??-??' '??:??:??' - '* ]]; then
    mv -v -- "$f" "$(date -r "$f" +%Y-%m-%d' '%H:%M:%S) - $f" ;
  fi;
done
Grzegorz Wierzowiecki
fonte
Se os seus arquivos timestamps são parafusados e são jpegs, você pode querer abetos para corrigi-los com dados EXIF: for f in *.jpg ; do jhead -ft "$f" ; donefonte: unix.stackexchange.com/a/290755/9689
Grzegorz Wierzowiecki