Gerar novo nome para o arquivo movido para evitar sobrescrever?

8

Como posso gerar um novo nome para um arquivo se houver um arquivo com o mesmo nome? Em um ambiente Desktop, um novo nome é gerado, acrescentando um número ao final do nome do arquivo, mas como isso pode ser feito a partir da linha de comando?

Estou usando o sistema operacional Android com o Busybox.

    
por kyle k 06.09.2013 / 18:45

3 respostas

5

Supondo que você tenha um shell POSIX, você pode fazer isso:

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 isso

Esta é uma função, portanto, salve-a no seu ~/.bashrc e chame-a como você faria com o normal mv .

O que isso faz

  • Armazena o caminho para o executável mv original na variável MV
  • Obtém o último argumento para o qual foi chamado na variável DEST
  • Se DEST existir e não for um diretório, essa função pressupõe que sua renomeação esteja tentando destruir um arquivo
  • Em seguida, ele extrai o prefixo do nome final (qualquer coisa antes do final . , que marca a extensão), a extensão (qualquer coisa após o final . ), a contagem (se houver alguma coisa no prefixo após a final - ).
  • A contagem extraída é definida como zero se nenhuma contagem for encontrada. Caso contrário, ela será definida para a contagem encontrada na etapa anterior.
  • A contagem atual é incrementada
  • A função chama-se então com todos os argumentos originais (comutadores + nomes de arquivo) menos o último e adiciona o novo nome de arquivo em vez do último argumento na chamada original. O novo nome é o nome antigo, mas com um contador de 3 dígitos (com zero-enchimento) adicionado antes da extensão.
  • A função é recursiva porque, se você estivesse dizendo mv a.txt b.txt , ele tentaria primeiro mv a.txt b-001.txt . Essa próxima chamada mv também deve ser a própria função, porque se b-001.txt também existir, queremos continuar incrementando o contador até encontrarmos um novo nome de arquivo que não existe.
  • Se o argumento final não existir ou for um diretório, o executável mv original será chamado com seus argumentos originais.

Advertências

  • O número de vezes que você pode repetidamente tentar destruir um arquivo existente depende do tamanho do contador (999 vezes, neste caso). Você pode escolher um número de dígitos que englobe o limite de inode em seu sistema de arquivos para garantir que isso funcione enquanto você for capaz de criar arquivos.
  • Se você tentar destruir um arquivo cujo nome seja semelhante a foo-001.txt , ele será movido para foo-001-001.txt .

Notas

  • Para alterar o padrão de nomenclatura, altere a 3 na instrução printf para o que quiser.
  • Este código foi testado
  • Isso é muito simplista e tenho certeza de que haverá casos extremos em que ele falhará miseravelmente. Estou feliz em tentar corrigi-los para você, se você encontrar algum. Enquanto isso, não tente isso em uma máquina de produção.
por 06.09.2013 / 21:52
3

Eu geralmente uso a ferramenta mktemp para criar arquivos temporários confiáveis. O padrão é criar arquivos, mas também pode criar diretórios por meio de sua opção -d .

Exemplo

Veja como você pode criar alguns nomes temporários para arquivos em seu diretório atual.

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

Isso criará os arquivos para você.

Referências

por 06.09.2013 / 20:45
2

Aqui está uma alternativa para o roteiro de Joseph R, que não tem nenhuma das advertências! Ele anexará um sufixo numérico a um nome de caminho (o caminho pode ser um diretório ou um arquivo), incrementando o valor do sufixo até ser encontrado um que ainda não existe. Outros utilitários como logrotate usam um padrão semelhante, mas rotacionam todas as cópias existentes para que o novo sempre tenha '0' para um sufixo. Como isso não é uma rotação nesse sentido, eu chamarei de dotmv . Lembre-se de que file.0 será a cópia mais antiga .

Por exemplo:

dotmv somefile.txt

Renomeia somefile.txt somefile.txt.0 , a menos que o último exista, caso em que será somefile.txt.1 e assim por diante. Você pode listar mais de um arquivo ( dotmv this that "the other thing" etc.), todos eles serão movidos por pontos.

Eu acredito que isso é compatível com POSIX - ele é executado com set -o posix no bash (mas isso é um teste duvidoso). Eu também testei com o shell android (jelly bean 4.2.1), e ele funciona lá. No entanto, no android você precisará alterar o shebang como indicado ou executá-lo sh dotmv - o que você vai de qualquer maneira, a menos que você tenha um dispositivo root, porque não há nenhuma maneira de tornar um script executável de outra forma. Mudar o shebang permitirá que você use 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

Espero que a lógica aqui seja bem direta. Isso pode ser implementado em Python, C ou qualquer outra linguagem procedural completa com E / S de arquivo.

    
por 07.09.2013 / 14:01