Rinominare un gruppo di file con data e ora della data modificata alla fine del nome file?


14

Quindi, diciamo, ho una directory con un mucchio di file come g.txt, dove g.txt è stato modificato l'ultima volta, diciamo, il 20 giugno 2012.

Come rinominerei in batch tutti i file (come g.txt) con l'ultima data di modifica del 20 giugno 2012 aggiunta alla fine?

Risposte:


13

Bash one-liner veloce e sporco per rinominare tutti i file (globbed) nella directory corrente da filename.txta filename.txt-20120620:

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

Un intraprendente secchione Bash troverà un caso limite per romperlo, ne sono sicuro. :)

Ovviamente, questo non fa cose desiderabili come controllare se un file ha già qualcosa che sembra una data alla fine.


2
+1, sembra buono. puoi renderlo un po 'più robusto usando' - 'come primo argomento di mv. guardie contro le file che iniziano con '-'
cas

5
Che tipo di dateimplementazione è quella? La mia GNU datenon sembra gestire l'input.
arte

coreutils 8.4 su RHEL 6.
jgoldschrafe,

2
@jgoldschrafe, sei sicuro che il codice funzioni correttamente per te? pastebin.com/nNYubf3A
arte

5
manatwork di buona cattura. la mia data GNU 8.13 non supporta neanche la lettura da stdin. Supporta l'utilizzo dell'ora mod di un file con "-r" - ad es. "Data +% Y% m% d -r $ f", quindi stat non è nemmeno necessario in questo caso particolare.
Cas

18

ecco una versione del one-liner di goldschrafe che:

  • non usa stat
  • funziona con le versioni precedenti della data GNU
  • gestisce correttamente tutti gli spazi nei nomi dei file
  • gestisce anche i nomi di file che iniziano con un trattino

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


1
Dovresti farlo date -r "$f" +%Y%m%do non funzionerà se POSIXLY_CORRECTè nell'ambiente. In generale, le opzioni dovrebbero andare prima di altri argomenti.
Stéphane Chazelas,

questa dovrebbe essere la "risposta corretta"
invalidusername

Ovviamente non hai bisogno delle parentesi graffe "${f}".
G-Man dice "Ripristina Monica" il

ma come lo faresti in modo tale che le date che abbiamo anteposto, ad esempio "Ymd-nomefile"?
fascia oraria

2

One-liner zsh obbligatorio (senza contare il caricamento singolo dei componenti opzionali):

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

Usiamo statil zsh/statmodulo incorporato dal modulo e la zmvfunzione per rinominare i file. Ed ecco un extra che posiziona la data prima dell'estensione, se presente.

zmv -n '(*)' '$1:r-$(stat -F %Y%m%d +mtime -- $1)${${1:e}:+.$1:e}'

1

Come ho capito, non sappiamo in anticipo qual è la data di modifica. Quindi abbiamo bisogno di ottenerlo da ogni file, formattare l'output e rinominare ogni file in modo che includa la data di modifica nei nomi dei file.

Puoi salvare questo script come qualcosa come "modif_date.sh" e renderlo eseguibile. Lo invochiamo con la directory di destinazione come argomento:

modif_date.sh txt_collection

Dove "txt_collection" è il nome della directory in cui abbiamo tutti i file che vogliamo rinominare.

#!/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

1
leggi la pagina man di stat (1) per scoprire come puoi ridurre la sceneggiatura di circa 3/4 ... la maggior parte di quanto sopra sta reinventando la ruota. in particolare, 'stat -c% Y' ti darà il tempo di modifica di un file in secondi dall'epoca (1970-01-01 00:00:00). Questo può quindi essere usato come input to date (1) per formattare il timestamp come richiesto. vedere la risposta di jgoldschrafe sopra per un esempio.
Cas

@CraigSanders Il lato positivo è che questo script funziona con sistemi operativi diversi da Linux, che hanno statun'utilità diversa o nessuna.
Gilles 'SO- smetti di essere malvagio' il

Si noti che l' analisi dell'output di lsnon è affidabile . Su alcune varianti unix, i nomi utente e gruppo possono contenere spazi, che elimineranno l'allineamento delle colonne della data.
Gilles 'SO- smetti di essere malvagio' il

@giles: vero, ma chiunque sia sensibile :-) installerebbe i coreutils GNU (e tutti gli altri strumenti GNU) su qualsiasi sistema non Linux. È il modo più semplice per ottenere non solo un buon set di strumenti per l'utente, ma anche un set coerente indipendentemente dal kernel sottostante.
Cas

1
(Continua) ... (5) Un altro bug: si presume che qualsiasi data / ora visualizzata ls come una data e un'ora (anziché una data e un anno) debba essere nell'anno corrente. Questo non è vero. È negli ultimi sei mesi; potrebbe essere l'anno precedente (ad esempio, dal 27 settembre 2016 al 31 dicembre 2016 negli ultimi sei mesi). (6) A rischio di pappagallo sul Wiki di Greg (che @Gilles ha già citato), i nomi di file che contengono newline (s) e spazi possono far fallire questo. ... (proseguendo)
G-Man dice "Ripristina Monica" il

0

Ecco la versione del one-liner di cas, (basato sull'eleliner di goldschrafe) estesa con idempotenza ,

vale a dire esteso ai file prefisso con data e ora e farlo solo per coloro che non hanno ancora il prefisso data e ora .

Caso d'uso: utile se si aggiungono nuovi file nella directory e si desidera aggiungere un prefisso di data e ora a quelli che non ne hanno ancora uno.

for f in * ; do
  if [[ "$f" != ????-??-??' '??:??:??' - '* ]]; then
    mv -v -- "$f" "$(date -r "$f" +%Y-%m-%d' '%H:%M:%S) - $f" ;
  fi;
done

Se i timestamp dei tuoi file sono sbagliati e sono jpeg, potresti voler abituarli a risolverli con i dati EXIF: for f in *.jpg ; do jhead -ft "$f" ; donefonte: unix.stackexchange.com/a/290755/9689
Grzegorz Wierzowiecki
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.