Générer un nouveau nom pour le fichier déplacé pour éviter l'écrasement?

8

Comment puis-je générer un nouveau nom pour un fichier s'il existe un fichier existant portant ce même nom? Dans un environnement de bureau, un nouveau nom est généré en ajoutant un numéro à la fin du nom de fichier, mais comment le faire à partir de la ligne de commande?

J'utilise le système d'exploitation Android avec Busybox.

kyle k
la source
1
Pouvez-vous développer votre Q pour spécifier en quelle capacité vous souhaitez créer ces fichiers par rapport à Android? Java? De quel outillage disposez-vous, busybox? Ou simplement vanille Android?
slm
@user slm Je vais l'utiliser pour sauvegarder les fichiers contenus dans le dossier de téléchargement sur ma tablette, le programme trie les fichiers en fonction de l'extension dans leurs dossiers respectifs, j'ai écrit un script python qui fait ça, mais le programme est lent et écrase également un fichier s'il porte le même nom. Je suis en train de réécrire le programme en bash, générer un nouveau nom est la partie avec laquelle je me débattais.
kyle k
Merci pour votre réponse! Avez-vous alors accès à une version de mktemp?
slm
@user slm J'ai installé busybox et il est inclus, mais si je fais un appel système à partir d'un programme mktempne fonctionnera pas.
kyle k
Ne fonctionne pas avec une sorte d'erreur? Aussi d'où faites-vous cet appel système? Python?
slm

Réponses:

5

En supposant que vous ayez un shell POSIX, vous pouvez le faire:

mv() {
        eval "DEST=\${$#}" #The destination is the last positional parameter
        if [ -e "$DEST" ] && ! [ -d "$DEST" ];then
                PREFIX=${DEST%.*}
                COUNT=1
                EXT=${DEST##*.}
                args= i=1
                while [ $i -lt $# ]; do args="$args \"\${$i}\"" i=$((i+1)); done
                DEST="$NAME-"$(printf "%03d" $COUNT)".$EXT"
                while [ -e "$DEST" ];do
                    COUNT=$((COUNT+1))
                    DEST="$NAME-"$(printf "%03d" $COUNT)".$EXT"
                done
                eval "command mv $args \"\$DEST\""
        else
                command mv "$@"
        fi
}

Comment l'utiliser

Il s'agit d'une fonction, alors enregistrez-la dans votre ~/.bashrcet appelez-la comme vous le feriez normalement mv.

Qu'est-ce que cela fait

  • Stocke le chemin vers l' mvexécutable d' origine dans la MVvariable
  • Obtient le dernier argument avec lequel il a été appelé dans la variable DEST
  • S'il DESTexiste et n'est pas un répertoire, cette fonction suppose que votre renommage essaie de clobber un fichier
  • Il extrait ensuite le préfixe du nom final (quoi que ce soit avant la finale ., qui marque l'extension), l'extension (quoi que ce soit après la finale .), le nombre (le cas échéant; quoi que ce soit dans le préfixe après la finale -).
  • Le compte extrait est mis à zéro si aucun compte n'a été trouvé sinon, il est réglé sur le compte trouvé à l'étape précédente.
  • Le nombre actuel est incrémenté
  • La fonction s'appelle ensuite avec tous les arguments d'origine (commutateurs + noms de fichiers) moins le dernier et ajoute le nouveau nom de fichier au lieu du dernier argument de l'appel d'origine. Le nouveau nom est l'ancien nom mais avec un compteur à 3 chiffres (avec remplissage nul) ajouté avant l'extension.
  • La fonction est récursive car si vous disiez, mv a.txt b.txtelle essaiera d'abord mv a.txt b-001.txt. Ce prochain mvappel doit également être la fonction elle-même car s'il b-001.txtexiste également, nous voulons continuer à incrémenter le compteur jusqu'à ce que nous trouvions un nouveau nom de fichier qui n'existe pas.
  • Si l'argument final n'existe pas ou est un répertoire, l' mvexécutable d' origine est appelé avec vos arguments d'origine.

Avertissements

  • Le nombre de fois que vous pouvez essayer à plusieurs reprises de clobber un fichier existant dépend de la longueur du compteur (999 fois dans ce cas). Vous pouvez choisir un nombre de chiffres qui englobe la limite d'inode sur votre système de fichiers pour vous assurer que cela fonctionnera aussi longtemps que vous pourrez créer des fichiers.
  • Si vous essayez d'alimenter un fichier dont le nom est similaire à foo-001.txt, il sera déplacé vers foo-001-001.txt.

Remarques

  • Pour modifier le modèle de dénomination, remplacez le 3dans l' printfinstruction par ce que vous voulez.
  • Ce code a été testé
  • C'est très simpliste et je suis sûr qu'il y aura des cas extrêmes où cela échouera misérablement. Je suis heureux d'essayer de les réparer si vous en trouvez. En attendant, n'essayez pas ceci sur une machine de production.
Joseph R.
la source
même esprit ici: P
Rahul Patil
@RahulPatil Sauf que j'essaie de dupliquer le comportement de l'interface graphique.
Joseph R.
c'est super, je n'ai pas encore utilisé GUI.
Rahul Patil
il écrase le fichier de destination s'il se termine
Rahul Patil
@RahulPatil Je ne sais pas trop ce que vous dites: dans votre exemple, il a identifié qu'il /tmps'agit d'un répertoire existant et a appelé /bin/mv file1 /tmpJe ne vois pas où est le problème.
Joseph R.
3

J'utilise généralement l'outil mktemppour créer des fichiers temporaires fiables. Par défaut, il crée des fichiers, mais peut également créer des répertoires via son -dcommutateur.

Exemple

Voici comment créer des noms temporaires de fichiers dans votre répertoire actuel.

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

Cela créera les fichiers pour vous.

Références

slm
la source
2
Non mktempsur Android, mais +1 de ma part, car c'est une bonne réponse à une bonne question, U&L sage.
goldilocks
@goldilocks - merci pour vos aimables paroles. C'est toujours sympa!
slm
@goldilocks - savez-vous par hasard si la boîte occupée est incluse? Il y a une implémentation de mktemp dans cela, je ne sais pas grand-chose sur Android. code.google.com/p/abb/source/browse/mktemp.c?name=upstream/…
slm
1
On dirait que busybox sur Android nécessite que l'appareil soit enraciné (jailbreaké). Le shell natif est /system/bin/shet semble être shcompatible - des constructions comme le if [ -w $file ]travail, mvet renamesont disponibles - donc un script pour faire cela juste avec des inserts devrait fonctionner là-bas.
goldilocks
@goldilocks - merci d'avoir vérifié. Que cherchez-vous pour le découvrir? Je voudrais me mouiller les pieds avec Android, mais je ne veux pas avoir de téléphone / appareil.
slm
2

Voici une alternative au script de Joseph R qui n'a aucune mise en garde! Il ajoutera un suffixe numérique à un nom de chemin (le chemin peut être un répertoire ou un fichier), en incrémentant la valeur du suffixe jusqu'à ce qu'il en trouve un qui n'existe pas déjà. D'autres utilitaires tels que logrotateutilisent un modèle similaire, mais font pivoter toutes les copies existantes de sorte que le nouveau ait toujours «0» pour un suffixe. Puisque ce n'est pas une rotation dans ce sens, je l'appellerai dotmv. N'oubliez pas que ce file.0sera la plus ancienne copie.

Par exemple:

dotmv somefile.txt

Renomme somefile.txt somefile.txt.0, sauf si ce dernier existe, auquel cas il le sera somefile.txt.1, etc. Vous pouvez répertorier plusieurs fichiers ( dotmv this that "the other thing"etc.), ils seront tous déplacés par points.

Je crois que c'est compatible POSIX - il fonctionne avec set -o posixbash (mais c'est un test douteux). J'ai également testé avec le shell Android (Jelly Bean 4.2.1), et cela fonctionne là-bas. Cependant, sur Android, vous devrez changer le shebang comme indiqué ou l'exécuter sh dotmv- ce que vous ferez de toute façon à moins que vous n'ayez un appareil enraciné, car il n'y a aucun moyen de rendre un script exécutable autrement. Changer le shebang vous permettra de l'utiliser exec dotmv.

#!/bin/sh
# On android change that to /system/bin/sh.

# Validate arguments
if [ $# -lt 1 ]; then
    echo "A list of one or more paths is required."
    exit 1
fi

# Checks if a path exists and can be moved.
checkPath () {
    if [ ! -e "$1" ]; then
        echo "'$1' does not exist."
        return 1;
    fi
    if [ ! -w "$1" ]; then
        echo "Cannot move '$1', permission denied."
        return 1;
    fi
    return 0;
}

# Finds a new path with numerical suffix.
getName () {
    suf=0;
    while [ -e "$1.$suf" ]
        do let suf+=1
    done
    Dest=$1.$suf
}

# Loop through arguments -- use quotes to allow spaces in paths.
while (($#)); do
    Src=$1
    Dest=$1
    shift
    checkPath "$Src"
    if [ $? -eq 0 ]; then
        getName "$Src"
        mv "$Src" "$Dest"
    fi
done

Espérons que la logique ici est très simple. Cela pourrait être implémenté en python, C ou tout autre langage procédural complet avec des E / S de fichiers.

boucle d'or
la source