Чтобы переместить файлы с одинаковыми именами и сохранить обе копии, перед перемещением задайте каждому входящему файлу свободное имя в папке назначения. В этом руководстве создаётся быстрая команда для Mac, которая добавляет нумерованные суффиксы; результат демонстрируется на временных текстовых файлах.

Речь идёт о перемещении обычных файлов из одной локальной папки в другую, где уже могут быть файлы с такими же именами. Существующие файлы в папке назначения остаются на месте; входящие файлы получают имена, например notes (2).txt. Сохранить обе копии означает, что после перемещения в папке назначения остаются оба файла: входящий файл больше не хранится в исходной папке.

Что Apple пишет о сохранении обоих файлов?

Apple документирует возможность запуска сценариев оболочки непосредственно в приложении «Быстрые команды». На общедоступных страницах Apple о «Быстрых командах», указанных здесь, для действия Move File не описана политика разрешения конфликтов Keep Both. Вместо того чтобы полагаться на недокументированный параметр, этот алгоритм явно выбирает имя в действии Run Shell Script. Apple описывает эту возможность на странице Знакомство с «Быстрыми командами» для macOS.

У базовой команды macOS более узкое документированное поведение: mv -n не перезаписывает существующий файл. Новое имя она не создаёт. Поэтому в этой быстрой команде нужны и цикл выбора имени, и параметр запрета перезаписи. Это две отдельные задачи, как показано в опубликованном Apple исходном тексте руководства по mv(1).

У объединения папок в Finder тоже есть ограничения. По словам Apple, вариант Merge появляется только тогда, когда в одной папке есть элементы, отсутствующие в другой; если в папках есть разные версии файлов с одинаковыми именами, доступны варианты Stop или Replace. Таким образом, объединение папок не обеспечивает нужное здесь поведение с нумерованными именами файлов. См. Упорядочивание файлов в папках на Mac.

Подготовьте временный пример с файлами одинакового имени

Используйте этот пример с двумя папками на одном локальном диске. Он обрабатывает обычные, не скрытые файлы непосредственно в исходной папке. Он не обходит вложенные папки, не перемещает псевдонимы или символические ссылки и не объединяет пакеты. Запускайте только один экземпляр за раз; ни один из процессов не должен менять эти папки.

Для быстрой команды требуется, чтобы действие Run Shell Script было доступно и запускалось на Mac. Если в вашей конфигурации это действие не работает, воспользуйтесь инструкцией для Finder далее в статье. Примерам ниже не требуется дополнительное программное обеспечение.

Откройте Terminal и вставьте эту команду настройки целиком. Она создаст на рабочем столе новую папку, содержащую только синтетические текстовые данные. Если папка Shortcuts-name-demo уже существует, команда остановится, не используя её содержимое повторно.

/bin/bash <<'BASH'
set -e
root="$HOME/Desktop/Shortcuts-name-demo"
/bin/mkdir "$root"
/bin/mkdir "$root/Incoming" "$root/Collected"
printf '%s\n' 'Incoming synthetic notes.' > "$root/Incoming/notes.txt"
printf '%s\n' 'Existing synthetic notes.' > "$root/Collected/notes.txt"
printf '%s\n' 'Earlier synthetic second copy.' > "$root/Collected/notes (2).txt"
printf '%s\n' 'Synthetic readme.' > "$root/Incoming/readme.txt"
printf 'Example folder: %s\n' "$root"
BASH

Конфликт создан намеренно: в папке назначения уже есть файлы notes.txt и notes (2).txt. Быстрая команда, которая просто добавляет (2), столкнётся с ещё одним конфликтом. Полезное правило: проверять последовательные варианты, пока не найдётся свободное имя.

Чтобы упростить проверку, включите параметр Show all filename extensions в разделе Finder > Settings > Advanced. Apple поясняет, что скрытие расширения меняет способ его отображения, а изменение самого расширения может повлиять на приложение, в котором открывается файл. В этом примере не удаляйте .txt. См. Отображение и скрытие расширений файлов на Mac.

Создайте быструю команду с явной обработкой конфликтов

Создайте быструю команду с помощью кнопки добавления в приложении «Быстрые команды» и назовите её Move Files and Keep Both. Найдите Run Shell Script в библиотеке действий и добавьте его. Apple описывает создание быстрых команд и добавление действий на странице Создание пользовательской быстрой команды на Mac.

Для действия задайте оболочку /bin/bash, а затем замените пример сценария приведённой ниже программой целиком. В этой версии пути к обеим папкам считываются из переменных сценария. Она не использует входные данные быстрой команды, выбранные элементы Finder или стандартный ввод, поэтому настройка передачи входных данных не имеет значения.

set -e
shopt -s nullglob

src_dir="$HOME/Desktop/Shortcuts-name-demo/Incoming"
dst_dir="$HOME/Desktop/Shortcuts-name-demo/Collected"

fail() {
    printf '%s\n' "$1" >&2
    exit 1
}

[[ -d "$src_dir" ]] || fail "Source folder does not exist."
[[ -d "$dst_dir" ]] || fail "Destination folder does not exist."

src_dir=$(cd "$src_dir" && pwd -P)
dst_dir=$(cd "$dst_dir" && pwd -P)
[[ "$src_dir" != "$dst_dir" ]] || fail "Choose different folders."

moved=0
for src in "$src_dir"/*; do
    if [[ -L "$src" || ! -f "$src" ]]; then
        printf 'Skipped non-regular item: %s\n' "$src" >&2
        continue
    fi

    name=${src##*/}
    stem=$name
    ext=
    if [[ "$name" == *.* && "$name" != .* && "$name" != *. ]]; then
        stem=${name%.*}
        ext=.${name##*.}
    fi

    target="$dst_dir/$name"
    number=2
    while [[ -e "$target" || -L "$target" ]]; do
        target="$dst_dir/$stem ($number)$ext"
        number=$((number + 1))
    done

    /bin/mv -n "$src" "$target" || fail "Move failed: $src"
    if [[ -e "$src" || -L "$src" || ! -f "$target" ]]; then
        fail "Move was not completed as expected: $src"
    fi

    moved=$((moved + 1))
    printf 'Moved: %s -> %s\n' "$name" "${target##*/}"
done
printf 'Completed: %s file(s) moved.\n' "$moved"

Добавьте действие Quick Look после действия со сценарием, чтобы посмотреть отчёт. Apple специально рекомендует вставлять Quick Look во время тестирования, чтобы видеть, какие данные передаются между действиями. См. Тестирование действий в приложении «Быстрые команды» на Mac.

Отчёт содержит текст с описанием выполненных перемещений. Это не список перемещённых объектов типа File. Если позже вы расширите быструю команду, не используйте строки отчёта как входные данные файлов. Apple указывает, что действия принимают и выдают определённые типы содержимого; используйте Show Info для действия, чтобы проверить, какие входные данные оно принимает и какие результаты выдаёт. См. Связи между действиями в приложении «Быстрые команды» на Mac.

Как правило именования сохраняет обе копии?

Для каждого исходного файла сценарий сначала проверяет исходный путь в папке назначения. Если по этому пути уже есть элемент, он проверяет (2), затем (3) и продолжает, пока не найдёт свободный вариант. Существующие папки и символические ссылки также считаются занятыми именами. В завершение выполняется перемещение с помощью mv -n — дополнительной проверки, запрещающей перезапись; она описана в документации Apple по mv(1).

Входящий файл Уже есть в папке назначения Выбранное имя в папке назначения
notes.txt Элемента с таким именем нет notes.txt
notes.txt notes.txt notes (2).txt
notes.txt notes.txt, notes (2).txt notes (3).txt
readme readme readme (2)
archive.tar.gz archive.tar.gz archive.tar (2).gz

Это правила именования программы, а не утверждение об автоматическом присвоении имён в Finder. Отделяется только последнее расширение: .gz остаётся в конце имени archive.tar (2).gz. Если входящий файл уже называется notes (2).txt, его основная часть имени сохраняется, и при необходимости получается notes (2) (2).txt. Сценарий не считает уже имеющееся число версией.

В программе пути заключаются в кавычки, а имена файлов перебираются после раскрытия оболочкой вместо разбиения текстового списка. Поэтому имена с пробелами обрабатываются как единые элементы. Синтаксис намеренно рассчитан на Bash; оставьте выбранной оболочку /bin/bash. В статье Apple Основы сценариев оболочки объясняется, почему различия между диалектами оболочки имеют значение.

Запустите пример и проверьте результат

Запустите быструю команду один раз, затем проверьте обе папки в Finder. При использовании предоставленных синтетических данных в папке назначения должны оказаться:

  • notes.txt, всё ещё содержащий Existing synthetic notes.
  • notes (2).txt, всё ещё содержащий Earlier synthetic second copy.
  • notes (3).txt, содержащий Incoming synthetic notes.
  • readme.txt, содержащий Synthetic readme.

Папка Incoming теперь должна быть пустой, а отчёт должен завершаться строкой Completed: 2 file(s) moved. При повторном запуске быстрой команды без добавления файлов она должна сообщить, что перемещений не было. Это ожидаемые результаты для данного примера; полный рабочий процесс в «Быстрых командах» macOS не запускался при подготовке этой статьи. Выражения для разделения имён файлов отдельно проверили на синтетических именах.

Если быстрая команда остановилась, проверьте обе папки, прежде чем запускать её снова. Каждый файл перемещается отдельно, поэтому ошибка при обработке следующего файла не отменяет предыдущих перемещений. Сценарий проверяет, остался ли исходный файл после каждой команды, а не предполагает, что завершение любой команды означает, что файл перемещён.

При обычной ошибке перемещения Apple рекомендует проверить, открыт ли файл и достаточно ли свободного места в папке назначения. См. Если не удаётся переместить или скопировать объект на Mac. Если после добавления суффикса имя файла станет слишком длинным, программа также не сможет выполнить это перемещение; она остановится, а не станет обрезать имя.

Проверка существования и перемещение — разные операции. Эта программа рассчитана на стабильные папки в локальном рабочем процессе, который запускает вручную один человек. Она не резервирует имена для возможных изменений, происходящих одновременно. Не запускайте несколько экземпляров одновременно и не допускайте изменений папок примера другими процессами.

Воспользуйтесь Finder, если конфликтов всего несколько

Для небольшой группы файлов явно переименуйте входящий файл перед перемещением. В синтетическом примере откройте Collected, выберите Incoming/notes.txt, нажмите Return, введите notes (3).txt и снова нажмите Return. Apple описывает процедуру переименования в статье Переименование файлов, папок и дисков на Mac.

Затем перетащите переименованный файл в Collected на том же диске. Apple описывает перетаскивание элемента в папку для его перемещения, копирование с удержанием Option и перемещение на другой диск с удержанием Command во время перетаскивания. См. Упорядочивание файлов в папках на Mac. Если появится ещё один конфликт имён, отмените передачу и выберите другое свободное имя.

Когда временный пример заработает на вашем Mac ожидаемым образом, измените только src_dir и dst_dir для другой пары локальных папок с теми же условиями. Не меняйте цикл перебора нумерованных имён и проверки завершения: выбор свободного имени сохраняет обе копии в папке назначения, а проверка результата показывает, действительно ли входящий файл переместился.

Источники