¿Generar un nuevo nombre para el archivo movido para evitar sobrescribir?

8

¿Cómo puedo generar un nuevo nombre para un archivo si hay un archivo existente con ese mismo nombre? En un entorno de escritorio, se genera un nuevo nombre al agregar un número al final del nombre del archivo, pero ¿cómo se puede lograr esto desde la línea de comandos?

Estoy usando el sistema operativo Android con Busybox.

kyle k
fuente
1
¿Puede ampliar su Q para especificar en qué capacidad desea hacer estos archivos con respecto a Android? ¿Java? ¿Qué herramientas tienes, busybox? ¿O solo Android vainilla?
slm
@user slm Voy a usarlo para hacer una copia de seguridad de los archivos contenidos en la carpeta de descarga en mi tableta, el programa clasifica los archivos basados ​​en la extensión en las carpetas respectivas, he escrito un script de Python que hace esto, pero el programa es lento y también sobrescribe un archivo si tiene el mismo nombre. Estoy en el proceso de reescribir el programa en bash, generar un nuevo nombre es la parte con la que estaba luchando.
kyle k
Gracias por tu respuesta! ¿Tienes acceso a una versión de mktemp?
slm
@user slm Tengo busybox instalado y está incluido, pero si hago una llamada al sistema desde un programa mktempno funcionará.
kyle k
¿No funciona con algún tipo de error? ¿También desde dónde está haciendo esta llamada al sistema? ¿Pitón?
slm

Respuestas:

5

Suponiendo que tiene un shell POSIX, puede hacer esto:

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
}

Como usar esto

Esta es una función, así que guárdela en su ~/.bashrcy llámela como lo haría normalmente mv.

Que hace esto

  • Almacena la ruta al mvejecutable original en la MVvariable
  • Obtiene el último argumento con el que se llamó a la variable DEST
  • Si DESTexiste y no es un directorio, esta función asume que su cambio de nombre está tratando de bloquear un archivo
  • Luego extrae el prefijo del nombre final (cualquier cosa antes de la final ., que marca la extensión), la extensión (cualquier cosa después de la final .), el conteo (si lo hay; cualquier cosa en el prefijo después de la final -).
  • El recuento extraído se establece en cero si no se encontró ningún recuento, de lo contrario, se establece en el recuento encontrado en el paso anterior.
  • El conteo actual se incrementa
  • La función se llama a sí misma con todos los argumentos originales (modificadores + nombres de archivo) menos el último y agrega el nuevo nombre de archivo en lugar del último argumento en la llamada original. El nuevo nombre es el antiguo pero con un contador de 3 dígitos (con relleno cero) agregado antes de la extensión.
  • La función es recursiva porque si estabas diciendo mv a.txt b.txtque primero lo intentará mv a.txt b-001.txt. Esta próxima mvllamada también debe ser la función en sí misma porque si b-001.txttambién existe, queremos seguir incrementando el contador hasta que encontremos un nuevo nombre de archivo que no existe.
  • Si el argumento final no existe o es un directorio, mvse llama al ejecutable original con sus argumentos originales.

Advertencias

  • El número de veces que puede intentar repetidamente bloquear un archivo existente depende de la longitud del contador (999 veces en este caso). Puede elegir una cantidad de dígitos que abarque el límite de inodo en su sistema de archivos para asegurarse de que esto funcione mientras pueda crear archivos.
  • Si intenta bloquear un archivo cuyo nombre es similar foo-001.txt, se moverá a foo-001-001.txt.

Notas

  • Para cambiar el patrón de nomenclatura, cambie el 3en la printfdeclaración a lo que quiera.
  • Este código ha sido probado
  • Esto es muy simplista y estoy seguro de que habrá casos extremos donde falle miserablemente. Estoy feliz de intentar solucionarlos si encuentra alguno. Mientras tanto, no intentes esto en una máquina de producción.
Joseph R.
fuente
misma mente aquí: P
Rahul Patil
@RahulPatil Excepto que intento duplicar el comportamiento de la GUI.
Joseph R.
eso es genial, todavía no he usado GUI.
Rahul Patil
está sobrescribiendo el archivo de destino si sale, he probado paste.ubuntu.com/6071897
Rahul Patil el
@RahulPatil No estoy seguro de lo que está diciendo: en su ejemplo, identificó que /tmpes un directorio existente y llamó /bin/mv file1 /tmpNo veo dónde está el problema.
Joseph R.
3

Generalmente uso la herramienta mktemppara crear archivos temporales confiables. El valor predeterminado es crear archivos, pero también puede crear directorios a través de su -dconmutador.

Ejemplo

Así es como puede crear algunos nombres temporales para los archivos en su directorio actual.

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

Esto creará los archivos por ti.

Referencias

slm
fuente
2
No mktempen Android, pero hago un +1 de todos modos ya que esta es una buena respuesta a una buena pregunta, U&L sabio.
Ricitos de oro
@goldilocks: gracias por sus amables palabras. ¡Siempre es lindo!
slm
@goldilocks: ¿sabe de antemano si busybox está incluido? Hay una implementación de mktemp en eso, no sé mucho sobre Android. code.google.com/p/abb/source/browse/mktemp.c?name=upstream/…
slm
1
Parece que busybox en Android requiere que el dispositivo esté rooteado (jailbreak). El shell nativo es /system/bin/shy parece ser shcompatible, construcciones como el if [ -w $file ]trabajo mvy renameestán disponibles, por lo que un script para hacer esto solo con funciones integradas debería funcionar allí.
Ricitos de oro
@goldilocks: gracias por revisar. ¿Qué estás buscando para descubrir esto? Me gustaría mojarme los pies con Android, pero no quiero tener un teléfono / dispositivo.
slm
2

¡Aquí hay una alternativa al guión de Joseph R que no tiene ninguna de las advertencias! Agregará un sufijo numérico a un nombre de ruta (la ruta puede ser un directorio o un archivo), incrementando el valor del sufijo hasta encontrar uno que aún no existe. Otras utilidades, como el logrotateuso de un patrón similar, pero rotan todas las copias existentes para que la nueva siempre tenga '0' para un sufijo. Como esto no es una rotación en ese sentido, lo llamaré dotmv. Solo recuerda que file.0será la copia más antigua .

Por ejemplo:

dotmv somefile.txt

Renombra somefile.txt somefile.txt.0, a menos que este último exista, en cuyo caso será somefile.txt.1, y así sucesivamente. Puede enumerar más de un archivo ( dotmv this that "the other thing"etc.), todos se moverán por puntos.

Creo que esto es compatible con POSIX: se ejecuta con set -o posixon bash (pero esa es una prueba dudosa). También probé con el shell de Android (Jelly Bean 4.2.1), y funciona allí. Sin embargo, en Android deberá cambiar el shebang como se indica o ejecutarlo sh dotmv, lo que de todos modos lo hará a menos que tenga un dispositivo rooteado, porque de lo contrario no hay forma de hacer que un script sea ejecutable. Cambiar el shebang te permitirá 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

Esperemos que la lógica aquí sea muy directa. Esto podría implementarse en python, C o en cualquier otro lenguaje de procedimiento completo con E / S de archivo.

encerrada dorada
fuente