외부 문자열 조작 도구를 사용하여 여러 폴더의 이름 바꾸기

외부 문자열 조작 도구를 사용하여 여러 폴더의 이름 바꾸기

나는 사용하고있다사례.

다음 명령이 유효합니다.

$ mv camelCase (ccase -t Kebab camelCase)

이제 다음을 사용하여 여러 디렉터리의 이름을 바꾸려고 합니다.

$ find . -type d -execdir rename 's/(.*)/$(ccase -t Kebab $1)/' '{}' \+

작동하지 않습니다. 다음 오류 메시지가 나타납니다.

Can't rename ./camelCase3 1000 4 24 27 30 46 121 132 1000ccase -t Kebab ./camelCase3): No such file or directory

어떡해?

답변1

해야 한다:

find . -depth ! -name . -type d -execdir sh -c '
  for dir do
    dir=${dir#*/} # remove the ./ prefix that some find implementations add
    new_dir=$(ccase -t Kebab -- "$dir") || continue
    [ "$dir" = "$new_dir" ] ||
      mv -i -- "$dir" "$new_dir"
  done' sh {} +

안 돼요그것들을 {}에 포함시키세요암호sh/ 또는 언어 해석기에 대한 인수 bash로 인해 명령 주입 취약점이 발생합니다. 바라보다"find -exec sh -c"를 사용해도 안전합니까?

기타 참고사항:

  • 변수 데이터가 로 시작하지 않을 것이라고 보장할 수 없는 경우 옵션의 끝을 표시하는 -데 사용합니다 . 지원되는지 --여부는 알 수 없습니다 . 그렇지 않은 경우 관리자에게 버그로 보고할 수 있습니다.ccase--
  • 여기서는 필요하지 않습니다 bash. 시스템이면 sh충분합니다.
  • bash와 마찬가지로 sh에서도 매개변수 확장 및 명령 대체는 최소한 목록 컨텍스트에서 인용되어야 합니다. 그렇지 않으면 분할+glob을 거치게 됩니다! 예를 들어 참조하십시오.bash/POSIX 쉘에서 변수를 인용하는 것을 잊어버리는 보안 위험
  • 특히 호출과 같이 잠재적으로 파괴적인 작업을 수행하는 경우 mv실행하는 모든 명령이 성공했는지 확인하는 것이 좋습니다. 여기서는 실패한 디렉터리 || continue건너뛰기를 사용합니다 .ccase
  • +가능한 경우 ;여러 디렉터리를 전달 하지 마십시오 . sh이렇게 하면 파일당 쉘이 호출되는 것을 방지할 수 있습니다. 그러나 find비표준 조건자를 지원 하는 모든 구현이 -execdir그렇게 하는 것은 아닙니다. 일부 BSD나 이전 버전의 findGNU 에서는 +동일한 작업을 수행 ;한 후 루핑하는 것이 중복됩니다(비록 무해하긴 하지만).
  • 에서는 이미 존재하는 mv -- SomeDir some_dir것과 동일하며 some_dir디렉토리이거나 디렉토리에 대한 심볼릭 링크입니다 mv -- SomeDir some_dir/SomeDir. GNU 구현을 사용하면 옵션을 사용하여 이를 방지 mv할 수 있습니다 (또는 아래 방법을 사용하여 충돌을 감지할 수 있습니다).-Tzmv

이를 방지 -execdir 하고 가능한 한 적은 수의 쉘을 실행하려면 이를 사용할 수 있지만 -exec기본 이름과 상위 디렉토리를 분리해야 합니다. 개선 사항으로 실패를 기록하여 ccase종료 상태 mv에 반영 할 수도 있습니다.find

find . -depth ! -name . -type d -exec sh -c '
  ret=0
  for dir do
    base=${base##*/}
    parent=${dir%/*}
    new_base=$(ccase -t Kebab -- "$base") && {
      [ "$base" = "$new_base" ] ||
        mv -i -- "$dir" "$parent/$new_base"
    } || ret=$?
  done
  exit "$ret"' sh {} +

위에서는 예 를 들어 두 파일이 동일한 새 이름으로 끝나는 경우와 같이 데이터 손실을 방지할 수 있는 옵션을 사용자에게 제공하는 옵션을 -i추가 했습니다 . 더 나은 접근 방식 은 모든 것을 미리 확인하고 시험 실행 모드( ) 를 제공하는 mvzsh를 사용하는 것입니다 .zmv-n

autoload -Uz zmv
zmv -n '(**/)(*)(#q/)' '$1$(ccase -t Kebab -- $2)'

또한 기본적으로 숨겨진 파일을 무시합니다. 기본적으로 깊이 우선 순회를 수행합니다. 이 경우 의 (#q/)종료-type d 상태는 ccase무시됩니다. 이름이 변경되지 않은 경우 이름 바꾸기가 시도되지 않습니다.

이름에 이미 하이픈이 포함된 파일을 처리하지 않으려면 (*)바꿀 수 있습니다 .(^*-*)

zsh에서 Kebab 케이스로 변환할 수도 있습니다.

zmv -n '(**/)(*)(#q/)' \
       '$1${${${2//[_[:space:]]##/-}//(#b)([^[:upper:].-])([[:upper:]])/$match[1]-$match[2]}:l}'

공백 또는 밑줄의 모든 시퀀스를 단일 문자로 변환하고, 대문자가 아닌 문자( 및 제외)와 기본 이름의 대문자 사이에 s를 삽입 -하고 전체 결과를 소문자로 변환합니다. 대신 로 이름을 바꾸려면 조정해야 합니다.-.-lAFileInTheUK.txta-file-in-the-u-k.txtafile-in-the-uk.txt

답변2

디렉터리의 이름을 하나씩 바꾸고 가장 깊은 디렉터리부터 시작합니다. 예를 들어

#!/bin/ksh93

find . -type d -depth -print | while read DIR ; do
    PREFIX=${DIR%/*}
    SUFFIX=${ ccase -t Kebab "${.sh.match:1}"; }
    print mv "${DIR}" "${PREFIX}/${SUFFIX}"
done

그 단어를 삭제하다인쇄스크립트가 요구 사항을 충족하는지 확인한 후 스크립트를 준비하세요 ;-).

답변3

여기 있어요. 내가 찾고 있는 명령은 다음과 같습니다.

find . -depth -type d -execdir bash -c 'mv {} $(ccase -t Kebab {})' \;

관련 정보