Создать новое имя для перемещенного файла, чтобы предотвратить перезапись?


8

Как я могу создать новое имя для файла, если существует существующий файл с тем же именем? В среде рабочего стола генерируется новое имя, добавляя число в конец имени файла, но как это можно сделать из командной строки?

Я использую операционную систему Android с Busybox.


1
Можете ли вы расширить свой Q, чтобы указать, в каком качестве вы хотите сделать эти файлы по отношению к Android? Ява? Какие инструменты у вас есть, busybox? Или просто ванильный андроид?
SLM

@user slm Я собираюсь использовать его для резервного копирования файлов, содержащихся в папке загрузки на моем планшете, программа сортирует файлы по расширению в соответствующие папки, я написал скрипт на python, который делает это, но программа работает медленно, а также перезаписывает файл, если он имеет то же имя. Я нахожусь в процессе переписывания программы на bash, генерация нового имени - это та часть, с которой я боролся.
Кайл К

Спасибо за ответ! Есть ли у вас доступ к версии mktemp?
SLM

@user slm Я установил busybox и он включен, но если я сделаю системный вызов из программы, mktempон не будет работать.
Кайл К

Не работает с какой-то ошибкой? Кроме того, откуда вы делаете этот системный вызов? Python?
СЛМ

Ответы:


5

Предполагая, что у вас есть оболочка POSIX, вы можете сделать это:

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
}

Как использовать это

Это функция, поэтому сохраните ее ~/.bashrcи вызовите как обычно mv.

Что это делает

  • Сохраняет путь к исходному mvисполняемому файлу в MVпеременной
  • Получает последний аргумент, с которым он был вызван, в переменную DEST
  • Если DESTсуществует и не является каталогом, эта функция предполагает, что ваше переименование пытается перерезать файл
  • Затем он извлекает префикс конечного имени (что-либо перед финалом ., которое обозначает расширение), расширение (что-либо после финала .), счетчик (если есть; что-нибудь в префиксе после финала -).
  • Извлеченный счетчик устанавливается равным нулю, если счет не был найден, в противном случае он устанавливается на счетчик, найденный на предыдущем шаге.
  • Текущий счет увеличивается
  • Затем функция вызывает себя со всеми исходными аргументами (переключатели + имена файлов) минус последний и добавляет новое имя файла вместо последнего аргумента в исходном вызове. Новое имя - это старое имя, но с 3-значным счетчиком (с нулями), добавленным перед расширением.
  • Функция рекурсивная, потому что, если вы говорите mv a.txt b.txt, сначала попытайтесь mv a.txt b-001.txt. Этот следующий mvвызов также должен быть самой функцией, потому что, если она b-001.txtтакже существует, мы хотим продолжать увеличивать счетчик, пока не найдем новое имя файла, которое не существует.
  • Если последний аргумент не существует или является каталогом, исходный mvисполняемый файл вызывается с вашими исходными аргументами.

Предостережения

  • Количество попыток повторного копирования существующего файла зависит от длины счетчика (в данном случае 999 раз). Вы можете выбрать количество цифр, которое включает ограничение на число узлов в вашей файловой системе, чтобы гарантировать, что это будет работать до тех пор, пока вы сможете создавать файлы.
  • Если вы попытаетесь скопировать файл, имя которого похоже foo-001.txt, он будет перемещен в foo-001-001.txt.

Ноты

  • Чтобы изменить шаблон именования, измените 3в printfвыражении то, что вам нравится.
  • Этот код был протестирован
  • Это очень упрощенно, и я уверен, что будут крайние случаи, когда это с треском провалится. Я с удовольствием попробую починить их для вас, если вы их найдете. В то же время, не пытайтесь сделать это на производственной машине.

то же самое здесь: P
Рахул Патил

@RahulPatil За исключением того, что я пытаюсь дублировать поведение GUI.
Джозеф Р.

это здорово, я еще не использовал GUI.
Рахул Патил

он перезаписывает файл назначения, если он выходит, я протестировал paste.ubuntu.com/6071897
Rahul Patil

@RahulPatil Не уверен, что вы говорите: в вашем примере он определил, что /tmpэто существующий каталог, и вызвал « /bin/mv file1 /tmpЯ не вижу, в чем проблема».
Джозеф Р.

3

Я обычно использую инструмент mktempдля создания надежных временных файлов. По умолчанию он создает файлы, но также может создавать каталоги с помощью -dпереключателя.

пример

Вот как вы можете создать временные имена для файлов в вашем текущем каталоге.

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

Это создаст файлы для вас.

Ссылки


2
Нет mktempна Android, но +1 от меня в любом случае, так как это хороший ответ на хороший вопрос, U & L мудрый.
Златовласка

@goldilocks - спасибо за добрые слова. Это всегда приятно!
SLM

@goldilocks - вы знаете, если включен busybox? В этом есть реализация mktemp, я не очень разбираюсь в Android. code.google.com/p/abb/source/browse/mktemp.c?name=upstream/...
ОДС

1
Похоже, busybox на Android требует, чтобы устройство рутировалось (взломано). Нативная оболочка совместима /system/bin/shи кажется shсовместимой - она if [ -w $file ]работает как работа mvи renameдоступна, поэтому сценарий для этого только со встроенными модулями должен работать там.
Златовласка

@goldilocks - спасибо за проверку. Что вы ищете, чтобы узнать это? Я хотел бы промочить ноги Android, но не хочу иметь телефон / устройство.
SLM

2

Вот альтернатива сценарию Джозефа Р., в котором нет ни одной оговорки! Он добавит числовой суффикс к имени пути (путь может быть каталогом или файлом), увеличивая значение суффикса до тех пор, пока не будет найден тот, который еще не существует. Другие утилиты, например, logrotateиспользуют аналогичный шаблон, но вращают все существующие копии, чтобы новая всегда имела «0» для суффикса. Поскольку это не вращение в этом смысле, я назову это dotmv. Просто помните, что file.0это будет самая старая копия.

Например:

dotmv somefile.txt

Переименовывает somefile.txt somefile.txt.0, если последнее не существует, в таком случае оно будет somefile.txt.1и так далее. Вы можете перечислить более одного файла ( dotmv this that "the other thing"и т. Д.), Все они будут перемещены точками.

Я считаю, что это POSIX-совместимый - он работает set -o posixна Bash (но это сомнительный тест). Я также тестировал с оболочкой android (jelly bean 4.2.1), и она там работает. Тем не менее, на Android вам нужно будет изменить shebang, как указано, или запустить его, sh dotmvчто вы будете делать в любом случае, если у вас нет рутованного устройства, потому что иначе невозможно сделать исполняемый скрипт. Изменение Шебанга позволит вам использовать 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

Надеюсь, логика здесь очень проста. Это может быть реализовано на python, C или любом другом полном процедурном языке с файловым вводом / выводом.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.