Renommer un tas de fichiers avec un horodatage à date modifiée à la fin du nom de fichier?

14

Donc, disons, j'ai un répertoire avec un tas de fichiers comme g.txt, où g.txt a été modifié pour la dernière fois, disons, le 20 juin 2012.

Comment renommer par lots tous les fichiers (comme g.txt) avec la dernière date de modification du 20 juin 2012 ajoutée à la fin?

InquilineKea
la source

Réponses:

13

Bash one-liner rapide et sale pour renommer tous les fichiers (globés) du répertoire actuel de filename.txtà filename.txt-20120620:

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

Un nerd Bash entreprenant trouvera un cas de bord pour le casser, j'en suis sûr. :)

Évidemment, cela ne fait pas de choses souhaitables comme vérifier si un fichier a déjà quelque chose qui ressemble à une date à la fin.

jgoldschrafe
la source
2
+1, semble bon. vous pouvez le rendre un peu plus robuste en utilisant '-' comme premier argument de mv. garde contre les filènes commençant par '-'
cas
5
De quel type de datemise en œuvre s'agit-il? Mon GNU datene semble pas gérer les entrées.
manatwork
coreutils 8.4 sur RHEL 6.
jgoldschrafe
2
@jgoldschrafe, êtes-vous sûr que le code fonctionne correctement pour vous? pastebin.com/nNYubf3A
manatwork
5
bonne gestion des captures. ma date GNU 8.13 ne supporte pas non plus la lecture depuis stdin. Il prend en charge l'utilisation de l'heure de modification d'un fichier avec "-r" - par exemple "date +% Y% m% d -r $ f", donc stat n'est même pas nécessaire dans ce cas particulier.
cas
18

voici une version du one-liner de goldschrafe qui:

  • n'utilise pas stat
  • fonctionne avec les versions antérieures de GNU date
  • s'adapte correctement à tous les espaces dans les noms de fichiers
  • gère également les noms de fichiers commençant par un tiret

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

cas
la source
1
Vous devriez le faire date -r "$f" +%Y%m%dou cela ne fonctionnera pas s'il POSIXLY_CORRECTest dans l'environnement. Généralement, les options doivent précéder les autres arguments.
Stéphane Chazelas
cela devrait être la "bonne réponse"
invalidusername
Bien sûr, vous n'avez pas besoin des accolades "${f}".
G-Man dit `` Réintègre Monica ''
mais comment feriez-vous pour que les dates que nous avons précédées, par exemple "Ymd-filename"?
timebandit
2

Obligatoire zsh one-liner (sans compter le chargement unique des composants optionnels):

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

Nous utilisons la fonction statintégrée du zsh/statmodule et la zmvfonction pour renommer les fichiers. Et voici un supplément qui place la date avant l'extension, le cas échéant.

zmv -n '(*)' '$1:r-$(stat -F %Y%m%d +mtime -- $1)${${1:e}:+.$1:e}'
Gilles 'SO- arrête d'être méchant'
la source
1

Si j'ai bien compris, nous ne savons pas à l'avance quelle est la date de modification. Nous devons donc l'obtenir à partir de chaque fichier, formater la sortie et renommer chaque fichier de manière à inclure la date de modification dans les noms de fichiers.

Vous pouvez enregistrer ce script sous quelque chose comme "modif_date.sh" et le rendre exécutable. Nous l'invoquons avec le répertoire cible comme argument:

modif_date.sh txt_collection

Où "txt_collection" est le nom du répertoire où nous avons tous les fichiers que nous voulons renommer.

#!/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
Une ligne
la source
1
lisez la page de manuel de stat (1) pour découvrir comment vous pouvez réduire votre script d'environ 3/4 ... la plupart des éléments ci-dessus réinventent la roue. en particulier, 'stat -c% Y' vous donnera le temps de modification d'un fichier en secondes depuis l'époque (1970-01-01 00:00:00). Cela peut ensuite être utilisé comme entrée à date (1) pour formater l'horodatage selon les besoins. voir la réponse de jgoldschrafe ci-dessus pour un exemple.
cas
@CraigSanders L'avantage est que ce script fonctionne sous des systèmes d'exploitation autres que Linux, qui ont un statutilitaire différent ou aucun.
Gilles 'SO- arrête d'être méchant'
Notez que l' analyse de la sortie de lsn'est pas fiable . Sur certaines variantes Unix, les noms d'utilisateur et de groupe peuvent contenir des espaces, ce qui annulera l'alignement des colonnes de date.
Gilles 'SO- arrête d'être méchant'
@giles: vrai, mais n'importe qui est sensé :-) installerait GNU coreutils (et tous les autres outils GNU) sur n'importe quel système non-linux. C'est le moyen le plus simple d'obtenir non seulement un bon ensemble d'outils utilisateur, mais également un ensemble cohérent quel que soit le noyau sous-jacent.
cas
1
(Suite) ... (5) Un autre bug: il suppose que toute date / heure affichée par ls comme une date et une heure ( au lieu d'une date et un an) doit être dans l'année en cours. Ce n'est pas vrai. C'est au cours des six derniers mois; cela pourrait être au cours de l'année précédente (par exemple, du 27 septembre 2016 au 31 décembre 2016 au cours des six derniers mois). (6) Au risque de perroqueter le Wiki de Greg (que @Gilles a déjà cité), les noms de fichiers contenant des sauts de ligne et des espaces peuvent provoquer l'échec de cette opération. … (Suite)
G-Man dit «Réintègre Monica»
0

Voici la version du one-liner de cas, (basé sur le oneliner de goldschrafe) étendu avec idempotence ,

c'est-à-dire étendu aux fichiers de préfixe avec date et heure et pour ce faire uniquement pour ceux qui n'ont pas encore de préfixe de date et heure .

Cas d'utilisation: utile si vous ajoutez de nouveaux fichiers dans le répertoire et souhaitez ajouter un préfixe date-heure à ceux qui n'en ont pas encore.

for f in * ; do
  if [[ "$f" != ????-??-??' '??:??:??' - '* ]]; then
    mv -v -- "$f" "$(date -r "$f" +%Y-%m-%d' '%H:%M:%S) - $f" ;
  fi;
done
Grzegorz Wierzowiecki
la source
Si vos horodatages de fichiers sont vissés et sont des jpeg, vous voudrez peut-être que les premiers les corrigent avec des données EXIF: for f in *.jpg ; do jhead -ft "$f" ; donesource: unix.stackexchange.com/a/290755/9689
Grzegorz Wierzowiecki