Generare un nuovo nome per il file spostato per impedire la sovrascrittura?


8

Come posso generare un nuovo nome per un file se esiste un file esistente con lo stesso nome? In un ambiente desktop viene generato un nuovo nome aggiungendo un numero alla fine del nome del file, ma come si può ottenere dalla riga di comando?

Sto usando il sistema operativo Android con Busybox.


1
Puoi espandere la tua Q per specificare in quale capacità vuoi creare questi file rispetto ad Android? Giava? Che strumenti hai, busybox? O semplicemente Android alla vaniglia?
slm

@user slm Lo userò per il backup dei file contenuti nella cartella di download sul mio tablet, il programma ordina i file in base all'estensione nelle rispettive cartelle, ho scritto uno script Python che fa questo, ma il programma è lento e sovrascrive anche un file se ha lo stesso nome. Sto riscrivendo il programma in bash, generare un nuovo nome è la parte con cui stavo lottando.
kyle k,

Grazie per la tua risposta! Hai accesso quindi a una versione di mktemp?
slm

@user slm Ho busybox installato ed è incluso, ma se faccio una chiamata di sistema da un programma mktempnon funzionerà.
kyle k,

Non funziona con qualche tipo di errore? Da dove stai effettuando questa chiamata di sistema? Pitone?
slm

Risposte:


5

Supponendo che tu abbia una shell POSIX, puoi farlo:

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
}

Come usarlo

Questa è una funzione, quindi salvala nel tuo ~/.bashrce chiamala come faresti normalmente mv.

Cosa fa questo

  • Memorizza il percorso mvdell'eseguibile originale nella MVvariabile
  • Ottiene l'ultimo argomento con cui è stato chiamato nella variabile DEST
  • Se DESTesiste e non è una directory, questa funzione presuppone che la ridenominazione stia tentando di bloccare un file
  • Quindi estrae il prefisso del nome finale (qualsiasi cosa prima del finale ., che segna l'estensione), l'estensione (qualsiasi cosa dopo il finale .), il conteggio (se presente; qualsiasi cosa nel prefisso dopo il finale -).
  • Il conteggio estratto viene impostato su zero se non è stato trovato alcun conteggio altrimenti, è impostato sul conteggio trovato nel passaggio precedente.
  • Il conteggio corrente viene incrementato
  • La funzione si chiama quindi con tutti gli argomenti originali (switch + nomi file) meno l'ultimo e aggiunge il nuovo nome file invece dell'ultimo argomento nella chiamata originale. Il nuovo nome è il vecchio nome ma con un contatore a 3 cifre (con zero-stuffing) aggiunto prima dell'estensione.
  • La funzione è ricorsiva perché se lo dicevi mv a.txt b.txtci proverà prima mv a.txt b-001.txt. La prossima mvchiamata deve essere anche la funzione stessa perché, se b-001.txtesiste, vogliamo continuare ad aumentare il contatore fino a quando non troviamo un nuovo nome di file che non esiste.
  • Se l'argomento finale non esiste o è una directory, l' mveseguibile originale viene chiamato con gli argomenti originali.

Avvertenze

  • Il numero di volte in cui puoi tentare ripetutamente di bloccare un file esistente dipende dalla lunghezza del contatore (in questo caso 999 volte). Puoi scegliere un numero di cifre che racchiude il limite di inode sul tuo filesystem per assicurarti che funzionerà finché sarai in grado di creare file.
  • Se si tenta di bloccare un file il cui nome è simile a foo-001.txt, verrà spostato in foo-001-001.txt.

Appunti

  • Per cambiare il modello di denominazione, modifica 3l' printfistruzione in come preferisci.
  • Questo codice è stato testato
  • Questo è molto semplicistico e sono sicuro che ci saranno casi limite in cui fallirà miseramente. Sono felice di provare a risolverli per te se ne trovi. Nel frattempo, non provarlo su una macchina di produzione.

stessa mente qui: P
Rahul Patil,

@RahulPatil Tranne che sto tentando di duplicare il comportamento della GUI.
Joseph R.,

fantastico, non ho ancora usato la GUI.
Rahul Patil,

sta sovrascrivendo il file di destinazione se esce, ho provato paste.ubuntu.com/6071897
Rahul Patil

@RahulPatil Non sono sicuro di quello che stai dicendo: nel tuo esempio, ha identificato che /tmpè una directory esistente e chiamato /bin/mv file1 /tmpNon vedo dove sia il problema.
Joseph R.,

3

In genere utilizzo lo strumento mktempper creare file temporanei affidabili. L'impostazione predefinita è la creazione di file, ma può anche creare directory, tramite il suo -dswitch.

Esempio

Ecco come è possibile creare alcuni nomi temporanei per i file nella directory corrente.

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

Questo creerà i file per te.

Riferimenti


2
No mktempsu Android, ma +1 da parte mia, dato che questa è una buona risposta a una buona domanda, saggia U&L.
Riccioli d'oro,

@goldilocks - grazie per le tue gentili parole. È sempre bello!
slm

@goldilocks: conosci con disinvoltura se la scatola occupata è inclusa? C'è un'implementazione di mktemp in questo, non so molto su Android. code.google.com/p/abb/source/browse/mktemp.c?name=upstream/…
slm

1
Sembra che busybox su Android richieda il root del dispositivo (jailbreak). La shell nativa è /system/bin/she sembra essere shcompatibile - costruisce come il if [ -w $file ]lavoro mve renamesono disponibili - quindi uno script per farlo solo con i built-in dovrebbe funzionare lì.
Riccioli d'oro,

@goldilocks - grazie per il controllo. Cosa stai cercando per scoprirlo? Mi piacerebbe bagnarmi i piedi con Android, ma non voglio avere un telefono / dispositivo.
slm

2

Ecco un'alternativa alla sceneggiatura di Joseph R che non presenta alcun avvertimento! Aggiungerà un suffisso numerico al nome di un percorso (il percorso può essere una directory o un file), incrementando il valore del suffisso fino a quando non ne viene trovato uno che non esiste già. Altre utility come logrotateusano un modello simile, ma ruotano tutte le copie esistenti in modo che quella nuova abbia sempre '0' per un suffisso. Dal momento che questa non è una rotazione in questo senso, la chiamerò dotmv. Ricorda solo che file.0sarà la copia più vecchia .

Per esempio:

dotmv somefile.txt

Rinomina somefile.txt somefile.txt.0, a meno che non esista quest'ultima, nel qual caso lo sarà somefile.txt.1, e così via. Puoi elencare più di un file ( dotmv this that "the other thing"ecc.), Tutti verranno spostati a punti.

Credo che questo sia conforme a POSIX - funziona con set -o posixsu bash (ma questo è un test dubbio). Ho anche testato con la shell android (jelly bean 4.2.1), e funziona lì. Tuttavia, su Android dovrai cambiare lo shebang come indicato o eseguirlo sh dotmv- cosa che farai comunque a meno che tu non abbia un dispositivo rooted, perché non c'è modo di rendere eseguibile uno script altrimenti. Cambiare lo shebang ti permetterà di usarlo 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

Spero che la logica qui sia molto semplice. Questo potrebbe essere implementato in Python, C o in qualsiasi altro linguaggio procedurale completo con I / O dei file.

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.