Gerar novo nome para o arquivo movido para evitar a substituição?

8

Como posso gerar um novo nome para um arquivo se houver um arquivo existente com o mesmo nome? Em um ambiente de área de trabalho, um novo nome é gerado, acrescentando um número ao final do nome do arquivo, mas como isso pode ser feito na linha de comando?

Estou usando o sistema operacional Android com o Busybox.

kyle k
fonte
1
Você pode expandir seu Q para especificar em qual capacidade você deseja criar esses arquivos em relação ao Android? Java? Quais ferramentas você possui, busybox? Ou apenas baunilha android?
Slm
@user slm Vou usá-lo para fazer backup dos arquivos contidos na pasta de download no meu tablet, o programa classifica os arquivos com base na extensão nas respectivas pastas, escrevi um script python que faz isso, mas o programa é lento e também substitui um arquivo se ele tiver o mesmo nome. Estou no processo de reescrever o programa no bash, gerar um novo nome é a parte com a qual eu estava lutando.
Kyle k
Obrigado pela resposta! Você tem acesso a uma versão do mktemp?
Slm
@user slm Tenho o busybox instalado e ele está incluído, mas se eu fizer uma chamada de sistema de um programa mktempnão funcionará.
Kyle k
Não funciona com algum tipo de erro? Também de onde você está fazendo essa chamada do sistema? Pitão?
Slm

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, então salve-a no seu ~/.bashrce chame-a como faria normalmente mv.

O que isso faz

  • Armazena o caminho para o mvexecutável original na MVvariável
  • Obtém o último argumento com o qual ele foi chamado na variável DEST
  • Se DESTexistir e não for um diretório, essa função assume que sua renomeação está tentando bloquear um arquivo
  • Em seguida, extrai o prefixo do nome final (qualquer coisa antes da final ., que marca a extensão), a extensão (qualquer coisa após a final .), a contagem (se houver; qualquer coisa no prefixo após a final -).
  • A contagem extraída é definida como zero se nenhuma contagem foi encontrada, caso contrário, é definida como a contagem encontrada na etapa anterior.
  • A contagem atual é incrementada
  • A função então se chama com todos os argumentos originais (switches + nomes de arquivos) 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 preenchimento zero) adicionado antes da extensão.
  • A função é recursiva porque se você estava dizendo mv a.txt b.txt, primeiro tentará mv a.txt b-001.txt. Essa próxima mvchamada também deve ser a própria função, porque, se b-001.txttambém existir, queremos continuar incrementando o contador até encontrarmos um novo nome de arquivo que não exista.
  • Se o argumento final não existir ou for um diretório, o mvexecutável original será chamado com seus argumentos originais.

Ressalvas

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

Notas

  • Para alterar o padrão de nomenclatura, altere o 3na printfdeclaração para o que quiser.
  • Este código foi testado
  • Isso é muito simplista e tenho certeza de que haverá casos extremos onde falhará miseravelmente. Fico feliz em tentar corrigi-los para você, se você encontrar algum. Enquanto isso, não tente fazer isso em uma máquina de produção.
Joseph R.
fonte
A mesma mente aqui: P
Rahul Patil
@RahulPatil Exceto que estou tentando duplicar o comportamento da GUI.
Joseph R.
isso é ótimo, ainda não usei a GUI.
Rahul Patil
ele está substituindo o arquivo de destino se ele sai, eu testei paste.ubuntu.com/6071897
Rahul Patil
@RahulPatil Não sei ao certo o que você está dizendo: no seu exemplo, identificou que /tmpé um diretório existente e chamou /bin/mv file1 /tmpNão vejo onde está o problema.
Joseph R.
3

Geralmente, uso a ferramenta mktemppara criar arquivos temporários confiáveis. O padrão é criar arquivos, mas também pode criar diretórios por meio de sua -dopção.

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

slm
fonte
2
Não mktempno android, mas +1 de qualquer maneira, já que essa é uma boa resposta para uma boa pergunta, U&L.
GOLDILOCKS
@ goldilocks - obrigado por suas amáveis ​​palavras. É sempre legal!
slm
@goldilocks - você sabe de antemão se o busybox está incluído? Há uma implementação do mktemp nisso, eu não sei muito sobre o Android. code.google.com/p/abb/source/browse/mktemp.c?name=upstream/…
slm
1
Parece que o busybox no Android exige que o dispositivo esteja enraizado (jailbroken). O shell nativo é /system/bin/she parece ser shcompatível - construções como o if [ -w $file ]trabalho mve renameestão disponíveis -, portanto, um script para fazer isso apenas com os built-ins deve funcionar lá.
GOLDILOCKS
@goldilocks - obrigado por verificar. O que você está procurando para descobrir isso? Gostaria de me molhar com o Android, mas não quero ter um telefone / dispositivo.
slm
2

Aqui está uma alternativa ao script de Joseph R, que não tem nenhuma das ressalvas! 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é que seja encontrado um que ainda não exista. Outros utilitários, como logrotateusam um padrão semelhante, mas giram todas as cópias existentes para que o novo sempre tenha '0' como sufixo. Como não é uma rotação nesse sentido, vou chamá-lo dotmv. Lembre-se de que file.0será a cópia mais antiga .

Por exemplo:

dotmv somefile.txt

Renomeia somefile.txt somefile.txt.0, a menos que 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.

Acredito que isso seja compatível com POSIX - ele roda set -o posixno bash (mas esse é um teste duvidoso). Também testei com o shell do Android (Jelly Bean 4.2.1) e funciona lá. No entanto, no Android, você precisará alterar o shebang conforme indicado ou executá-lo sh dotmv- o que você fará de qualquer maneira, a menos que tenha um dispositivo raiz, porque não há como tornar um script executável de outra forma. Alterar 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 muito direta. Isso pode ser implementado em python, C ou qualquer outra linguagem processual completa com E / S de arquivo.

Cachinhos Dourados
fonte