find 명령을 사용하여 파일 이름 바꾸기

find 명령을 사용하여 파일 이름 바꾸기

10K mp4 파일 목록이 있는데 그 중 일부에는 ? 태그가 포함되어 있습니다. _(밑줄)로 바꿔야 합니다.

find 명령을 사용하여 이러한 파일을 찾을 수 있지만 이름을 바꿀 수 있는지는 확실하지 않습니다.

find . -name "*\?*.mp4" -exec mv {} XXXXXXX \;

find에서 반환된 목록을 반복해야 하고 교체해야 할 각 목록을 반복해야 하는 것 같나요? _를 사용하지만 어떻게 해야 할지 모르겠습니다.

답변1

이를 위해 사용하지 마십시오 find. 어느 방향으로 가든 파일당 하나와 같은 것이 필요할 것입니다 find. mv과정이 많이 필요한데 아무 소용이 없습니다. 말할 것도 없이 그렇게 하는 것이 더 어렵습니다. 어쨌든 그건 내 의견이다.

나는 스트리밍 또는 일괄 처리 도구를 사용하며 다음을 선호하는 경향이 있습니다 pax.

cd -P . && mkdir ../newmp4 &&
pax -rwls'|?|_|g' . "${PWD%/*}/newmp4"

디렉토리를 생성할 수 있는 경우 ../newmp4이 작은 스크립트는 하드 링크를 사용하여 방금 생성한 디렉토리에 루트가 있는 트리를 미러링할 뿐만 아니라 .모든 파일 이름을 밑줄로 바꿉니다.?

이렇게 하면 얻을 수 있는 이점 중 하나는둘 다이후에도 트리는 계속 존재합니다. 현재 디렉터리의 루트에 있는 파일은 영향을 받지 않습니다. 트리의 미러링된 버전에서만 변경됩니다. 따라서 삭제할 트리 버전을 결정하기 전에 결과를 확인할 수 있습니다. 절차 중에 문제가 발생하더라도 해를 끼치 지 않습니다.

그러나 이를 위해서는 하드 링크를 지원하고 최소한 +2 하위 디렉토리만큼 남은 여유 inode가 있는 파일 시스템이 필요합니다 . 단, 모든 하위 디렉토리가 동일한 파일 시스템 마운트를 공유한다고 .가정합니다 ... ..

rsync나는 그것이 그렇게 간단하다고 생각하지 않지만 실제로 동일한 효과를 얻는 것이 가능하다고 생각합니다 . 당신이 가지고 있지 않다면pax (당신이진짜 ~해야 한다), 당신은 그것을 얻을 수 있습니다.미라빌로스유지하다(내가 아는 한)최대일반적으로 사용되는 버전리눅스 시스템에서.

답변2

개인적으로 나는 이것이 Perl의 요구 사항일 뿐이라고 생각합니다. 특수 문자 안전 및 이름 변경은 fork exec가 아닌 시스템 호출입니다.

find . -name "*\?*.mp4" -print0|perl -n0e 'next if /\?.*\//;$o=$_;s!\?!_!g;next if -e;rename $o, $_'

정의되지 않은 동작을 방지하고 이름 바꾸기 충돌을 확인하려면 ?가 있는 디렉터리의 파일을 무시하세요. 아래의 자세한 지침을 참조하세요.

find . \               # find files below the current directory
    -name "*\?*.mp4" \ # having a ? in the name and ending in .mp4
    -print0\           # print the file names separated by nulls
|perl \                # direct the output of find to the input of perl
    -n \               # loop over standard input while assigning it to $_
    -0 \               # lines from standard input are separated by null
    -e \               # evaluate the next argument as perl commands
    'next if /\?.*\//; # skip this file if the directory name includes ?
    $o=$_;             # make a copy of the file name of preserve the name
    s!\?!_!g;          # change the ?s in the filename to _s
    next if -e;        # skip file if there is a file by that name
    rename $o, $_'     # do the rename

답변3

내가 찾은 가장 쉬운 방법은 다른 것을 파이프로 연결하여 명령을 생성한 sed다음 에서 명령을 실행하는 것입니다 sh.

find . -name '*\?*.mp4' -print | sed 's/.*/"&";h;y/?/_/;x;G;s/\n/ /;s/^/mv /' | sh -s

find에서와 같이 설명이 매우 필요 합니다 sh. 이 sed명령에는 설명이 필요할 수 있습니다.

  • s/.*/"&"/공백이나 특수 문자를 처리하려면 입력을 큰따옴표로 묶으십시오.
  • h버퍼를 "보류" 버퍼에 복사하고 복사본을 저장합니다.
  • y/?/_/tr \? _명령줄과 동일합니다.
  • x"hold" 버퍼의 내용을 모드 버퍼로 바꿉니다.
  • G"\n" 구분 기호를 사용하여 패턴 버퍼의 끝에 "hold" 버퍼를 추가합니다. 이제 "stringwith?\nstringwith_"가 있습니다.
  • s/\n/ /개행 문자를 바꾸십시오.
  • s/^/mv /사전 명령 mv.

그러면 이전 이름과 새 이름을 사용하여 mv찾은 각 파일에 대한 명령이 생성 됩니다.find

답변4

그리고 zsh:

autoload zmv # best in ~/.zshrc
zmv '(**/)(*\?*.mp4)' '$1${2//\?/_}'

이는 숨겨진 디렉터리를 제외하고 숨겨진 디렉터리 내부를 살펴보지 않습니다. 유형(일반, 디렉토리, 심볼릭 링크...)에 관계없이 이러한 파일의 이름을 바꿉니다.

파일/디렉터리 숨기기, mp4 파일 이름 바꾸기(일반 파일인 경우)만 고려하고 대소문자를 구분하지 않으려면 다음과 같습니다.

zmv '(**/)(*\?*.(#i)mp4)(#q.D)' '$1${2//\?/_}'

GNU 근사치( zmv이전과 같이 충돌 및 재정의를 확인하지 않음):

find . -iname '*\?*.mp4' -type f -exec bash -c '
  for file do
    base=${file##*/}
    mv -i "$file" "${file%/*}/${base//\?/_}"
  done' {} +

관련 정보