파일 이름에서 아포스트로피의 이름을 반복적으로 바꿉니다'(Bash)

파일 이름에서 아포스트로피의 이름을 반복적으로 바꿉니다'(Bash)

파일에서 아포스트로피를 제거해야 합니다. 나는 여러 가지 접근 방식을 시도했습니다.스택 교환.

저는 Synology NAS를 사용하고 있어서 Python이나 Perl이 없고 특정 디렉터리를 제외해야 합니다(find -prune).

다음 find 명령은 테스트 중에 일반적으로 작동하는 것 같습니다(에코 사용).

find . -depth \( -type d -name "@*" -o -name "*#recycle*" -o -name "*SynoResource*" -prune \) -o \( -name "*\'*" -execdir bash -c 'for f; do echo mv ${f/\\\'/\\\\'} `echo $f | sed 's/\\\'/X/g'`; done' _ {} + \)

"911's.zip"과 같은 파일의 경우 아포스트로피 '를 X로 바꾸면 다음이 생성됩니다(위 명령 사용).

mv ./911's.zip ./911Xs.zip

명령을 실행하기 위해 "echo"를 제거하면 다음 오류가 발생합니다.

mv: target '911Xs.zip' is not a directory

대화형 셸에서는 다음을 사용하여 파일 이름을 바꿀 수 있습니다.

mv 911\'s.zip 911Xs.zip

물론 '( 와 같은 \'s가 여러 개 있는 경우에도 ${f/\'/X}) 탈출을 시도했지만 작동하지 않았습니다.

어떤 아이디어가 있나요?

감사합니다,

게리

답변1

몇 가지 문제:

  • 프로세스가 먼저 떠나기 때문에 -prune결합 할 수 없으므로 디렉토리의 전체 내용이 처리된 후에는 너무 늦어서 아무런 효과가 없습니다.-depth-depth-prune
  • 당신의 -prune위치가 잘못되었습니다. 어디에 넣으면 일치하는 파일에만 적용됩니다 -name "*SynoResource*". 동일 합니다. -type d에만 적용됩니다 -name "@*".
  • 매개변수 확장 및 명령 대체에 큰따옴표가 누락되어 분할+글로브가 발생합니다.
  • echo일반적으로 임의의 데이터를 출력하는 데 사용할 수 없습니다.
  • '내부에 작은따옴표 문자열이 있을 수 없습니다 . '리터럴 값을 명령에 전달 해야 하는 경우 다른 셸 인용 연산자( "..."또는 백슬래시)를 사용하고 작은따옴표 밖에서 인용해야 합니다.
  • '그것은 그들에게 특별한 것이 아니며 mv, 그것을 피할 필요도 없습니다. 이는 쉘에만 특별하며 강력한 참조 연산자입니다.sedfind
  • $(...)더 이상 사용되지 않는 대신 사용해야 합니다 `...`(백슬래시가 포함될 경우 더 복잡해집니다).

그래서 여기 있습니다:

LC_ALL=C Q="'" find . -depth \
  ! -path '*/@*' \
  ! -path '*#recycle*' \
  ! -path '*SynoResource*' \
  -name "*'*" -execdir bash -c '
    for file do
      mv -i -- "$file" "${file//$Q/X}"
    done' bash {} +

여기서는 -prune에서 작동하지 않기 때문에 ( GNU에서도 호출됨 ) -depth을 사용하여 전체 경로를 기반으로 제외된 디렉터리의 파일을 필터링합니다. 그러나 이는 성능 관점에서 이상적이지 않은 디렉터리로 이동한다는 의미입니다.-path-wholenamefindfind

를 사용한다는 것은 기본 이름만 바꾸면 괜찮다는 것을 의미하지만, s가 있는 파일이 포함된 모든 디렉터리 에서 최소한 하나를 실행 -execdir하게 된다는 의미이기도 합니다 . 또는 다음을 수행할 수 있습니다.bash'

LC_ALL=C Q="'" find . -depth \
  ! -path '*/@*' \
  ! -path '*#recycle*' \
  ! -path '*SynoResource*' \
  -name "*'*" -exec bash -c '
    for file do
      dir=${file%/*} base=${file##*/}
      mv -i -- "$file" "$dir/${base//$Q/X}"
    done' bash {} +

이렇게 하면 스크립트가 실행되는 동안 누군가가 파일과 디렉터리의 이름을 바꾸는 것에 대해 더 효율적이기는 하지만 덜 안전합니다.

연습 실행의 경우 mv명령을 다음으로 바꿀 수 있습니다.

(PS4="Would run"; set -x; : mv -- "$file" "$dir/${base//$Q/X}")

echobash이는 추적 출력에서 ​​모호하지 않게 하기 위해 필요한 곳에 따옴표를 사용하기 때문에 as를 사용하는 것보다 낫습니다 . 추적 출력은 동일한 명령을 실행하는 데 사용할 수 있는 유효한 쉘 코드처럼 보입니다.

여기에서 -exec바꾸기를 사용하면 -execdir이름이 바뀔 파일을 명확하게 추적할 수도 있습니다.

디렉터리 깊이를 먼저 사용하되 계속 처리하려면 출력에서 -prune​​GNU(및 사용 가능한 경우 GNU)를 사용하여 이를 반전시키는 또 다른 방법이 있습니다.tac -s ''xargsfind -print0

LC_ALL=C find . -depth \
  '(' -name '@*' -o \
      -name '*#recycle*' -o \
      -name '*SynoResource*' \
  ')' -type d -prune -o \
  -name "*'*" -print0 |
  tac -s '' |
  Q="'" xargs -r0 bash -c '
    for file do
      dir=${file%/*} base=${file##*/}
      mv -i -- "$file" "$dir/${base//$Q/X}"
    done' bash {} +

GNU는 없지만 bash가 tac4.4 xargs이상인 경우 다음을 수행할 수도 있습니다.

LC_ALL=C find . -depth \
  '(' -name '@*' -o \
      -name '*#recycle*' -o \
      -name '*SynoResource*' \
  ')' -type d -prune -o \
  -name "*'*" -print0 |
  Q="'" bash -c '
    readarray -td "" files &&
      for (( i = ${#files[@]} - 1; i >= 0; i-- )); do
        file=${files[i]}
        dir=${file%/*} base=${file##*/}
        mv -i -- "$file" "$dir/${base//$Q/X}"
      done' bash {} +

(아직 테스트해본 적은 없으니 참고해주세요.)

관련 정보