충돌 없이 파일 이름 번호를 이동하는 방법은 무엇입니까?

충돌 없이 파일 이름 번호를 이동하는 방법은 무엇입니까?

다음 파일 목록을 고려하십시오.

$ touch index-{1,2,3,4,5,6,7,8,9}.txt

내가 그걸 바꾸고 싶다면아래에그래서 그들은 처음부터 시작합니다.비교적 쉬움:

$ rename --verbose 's/^index-([1-9])\.txt$/$1/; $_="index-" . ($_ - 1) . ".txt"' index-*.txt

man bash이는 지정된 glob이 알파벳순으로 정렬되어 다음과 같이 이름이 바뀌기 때문에 작동합니다 index-1.txt.index-0.txt 앞으로index-2.txt로 이름이 변경되었습니다 index-1.txt.

변경하려는 경우 실패합니다.위로, 또는 번호가 다음과 같은 경우다른 길이:

$ touch index-{10,11}.txt
$ rename --verbose 's/^index-([0-9]+)\.txt$/$1/; $_="index-" . ($_ + 1) . ".txt"' index-*.txt
index-10.txt not renamed: index-11.txt already exists
index-11.txt renamed as index-12.txt
index-1.txt not renamed: index-2.txt already exists

가능한 장기 수정 사항:

  • rename충돌하는 옵션이 발생하지 않을 때까지 작업 순서를 다시 지정해 보십시오.
  • rename먼저 파일을 임시 디렉터리로 이동한 다음 새 이름으로 다시 이동하는 옵션입니다.
  • rename첫 번째 단계에서 원래 이름이나 새 이름과 충돌하지 않는 고유한 이름을 사용하여 두 단계로 파일 이름을 바꾸는 옵션입니다 .
  • 자연스러운 정렬을 하는 방법 +뒤집다Bash 전역 변수.

이 문제를 해결하는 쉬운 방법이 있나요?

답변1

일련의 이름 바꾸기가 어떤 순서로 이루어져야 하는지 결정하기 위한 일반적인 솔루션을 찾는 데 도움이 될 수 있는 Unix에는 잘 알려지지 않은 표준 명령이 있습니다.tsort

다음과 같은 파일에 수행할 이름 바꾸기 목록이 있다고 가정해 보겠습니다 renames.txt(시연을 위해 해당 이름에 공백이 포함되어 있지 않다고 가정).

d a
e f
b e
a c

d이름이 변경되기 때문에 먼저 이름을 변경해야 한다는 a의미입니다 . 따라서 파일 이름을 바꿔야 하는 순서와 반대인 부분 정렬 순서가 있습니다.ad

tsort정렬 순서의 부분 목록에서 전체 정렬 순서를 유추하는 도구입니다. 루프가 있으면 오류를 반환하므로 해결책이 없는 상황을 감지하는 데 도움이 됩니다. 해당 입력을 적용하면 tsort다음이 제공됩니다.

b
d
e
a
f
c

after 이후에 이름을 바꿔야 b한다고 나와 있습니다 . 우리는 사용할 수 있습니다de암소 비슷한 일종의 영양 tac(일부 시스템에서도 사용 가능 tail -r) 순서를 반대로 하려면:

c
f
a
e
d
b

그리고 이름 변경 목록에 추가하세요.

tsort renames.txt | tac | awk '
  NR==FNR {
    ren[$1] = $2
    next
  }
  $1 in ren {
    print "mv -i --", $1, ren[$1]
  }' renames.txt -

이는 우리에게 다음을 제공합니다.

mv -i -- a c
mv -i -- e f
mv -i -- d a
mv -i -- b e

그런 다음 파이프라인을 통해 실행할 수 있습니다 sh.

그러나 위의 코드는 루프를 감지하기 위해 위의 종료 상태를 확인하지 않았 tsort으며 파일 이름에 특수 쉘 문자가 포함되어서는 안 되므로 강력하지 않습니다.

이것견고성독자를 위한 연습으로 남겼습니다 ;-)

답변2

더 나은 견고성을 위해 다시 작성된 @l0b0의 솔루션:

printf '%s\0' index-*.txt |
  sort --zero-terminated --field-separator - --key 2rn |
  xargs -0r rename --verbose '
    s/^index-([0-9]+)\.txt$/$1/;
    $_="index-" . ($_ + 1) . ".txt"'

솔루션을 자유롭게 추가해 주시면 답변을 삭제하겠습니다.

@l0bo의 솔루션은 Unix가 아닌 GNU 전용입니다(GNU는 Unix가 아님).

답변3

이는 위의 특정 사례에 적용됩니다.

rename --verbose 's/^index-([0-9]+)\.txt$/$1/; $_="index-" . ($_ + 1) . ".txt"' $(printf '%s\n' index-*.txt | sort --field-separator - --key 2n | tac)

하지만:

  1. 그것은 처리하지 않습니다공백파일 이름에
  2. 임의의 파일 이름의 경우 상황은 훨씬 더 복잡합니다.

답변4

맥락을 설명하자면, 타임랩스용 LapseIt Pro 앱을 사용하고 있었는데 어떤 이유로 중지되었다가 타임랩스를 다시 시작했지만 다음과 같은 다른 프로젝트 이름이 지정되었습니다.

Proj10_img0000000x

3000개 정도의 파일 이름을 모두 이전 프로젝트 Proj9_img0000000x로 바꾸고 마지막에 번호를 수정하여 프로젝트가 연속적인 이름 + 번호 피드를 가지도록 해야 모든 이미지를 하나의 프로젝트 비디오로 렌더링할 수 있습니다.

Proj10_img00000001 --> Proj10_img00003249를 Proj9_img00004325 --> Proj9_img00007574로 변경해야 합니다.

요약하면 최종 결과는 프로젝트 폴더 Proj9_img00000001 --> Proj9_img00007574에서 다음과 같습니다.

Mac OSX를 사용하는 경우(저는 Mojave를 사용하고 있습니다)

  1. Finder에서 수정할 모든 파일을 강조 표시합니다.
  2. 강조 표시된 파일 부분을 마우스 오른쪽 버튼으로 클릭하세요.
  3. "X 수량" 항목 이름 바꾸기를 선택하세요.
  4. "Finder 항목 이름 바꾸기" 팝업이 열립니다.
  5. 이 팝업을 사용하면 다른 편리한 옵션과 함께 쉽게 이름을 바꾸고 번호를 다시 매길 수 있습니다.

여전히 명령줄 grep/sed/awk/cut & find 명령을 보고 있지만 지금은 위의 내용이 작동합니다(게시하려면 최대한 빨리 비디오를 완료해야 합니다).

관련 정보