이름이 같은 파일을 둘 다 유지하면서 옮기려면, 옮기기 전에 새로 들어오는 각 파일에 대상 폴더에서 사용하지 않는 이름을 지정하세요. 이 가이드에서는 번호가 붙은 접미사를 추가하는 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에서 폴더의 파일 정리하기를 참조하세요.

이름이 같은 파일이 있는 임시 예제 준비하기

같은 로컬 디스크에 있는 두 폴더를 사용해 이 예제를 진행하세요. 원본 폴더 바로 안에 있는 일반 파일과 숨김 파일이 아닌 파일을 처리합니다. 하위 폴더를 재귀적으로 처리하거나, 별칭 또는 심볼릭 링크를 옮기거나, 패키지를 병합하지 않습니다. 한 번에 한 인스턴스만 실행하고, 실행 중에는 다른 프로세스가 두 폴더를 변경하지 않도록 하세요.

이 단축어를 사용하려면 Mac에서 Run Shell Script를 사용할 수 있고 실행할 수 있어야 합니다. 설정상 해당 동작을 실행할 수 없다면 이 문서 뒤에 나오는 Finder 절차를 사용하세요. 아래 예제에는 추가 소프트웨어가 필요하지 않습니다.

터미널을 열고 다음 설정 명령 전체를 붙여넣으세요. 이 명령은 합성 텍스트만 들어 있는 새 데스크탑 폴더를 만듭니다. 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)을 덧붙이는 단축어는 또 다른 충돌을 만나게 됩니다. 유용한 규칙은 사용 가능한 이름이 나올 때까지 후보 이름을 차례로 확인하는 것입니다.

파일 이름을 쉽게 확인하려면 Finder > Settings > Advanced에서 Show all filename extensions를 활성화하세요. 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)를 시도하며 사용 가능한 후보가 나올 때까지 계속합니다. 기존 폴더와 심볼릭 링크도 이미 사용 중인 이름으로 간주합니다. 마지막 이동에는 Apple의 mv(1) 문서에 따라 추가 덮어쓰기 방지 검사로 mv -n을 사용합니다.

새로 들어오는 파일 대상 폴더에 이미 있는 항목 선택된 대상 이름
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.로 끝나야 합니다. 파일을 추가하지 않은 채 단축어를 다시 실행하면 이동한 파일이 0개라고 보고해야 합니다. 이는 이 예제의 예상 결과입니다. 이 문서를 준비할 때 전체 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만 수정하세요. 번호가 붙은 이름을 선택하는 반복문과 완료 여부 확인을 함께 유지하세요. 사용하지 않는 이름을 선택하면 대상 폴더의 두 파일이 모두 보존되고, 결과를 확인하면 새로 들어온 파일이 실제로 이동했는지 알 수 있습니다.

출처