디렉터리의 모든 디렉터리에 밑줄 문자 접두어 추가

디렉터리의 모든 디렉터리에 밑줄 문자 접두어 추가

이름 시작 부분에 밑줄 "_" 문자를 포함하도록 이름을 바꿔야 하는 디렉터리 내에 여러 디렉터리가 있습니다.

테스트로서 내용에 세 개의 디렉토리가 있는 상위 디렉토리를 만들었습니다.

dir1, dir2 and dir3; three files: file1, file2, file3

세 개의 점 접두사 파일:

.file1, .file2, .file3 

지금까지 Mac에 gfind설치한 후 사용해 보았습니다 .findutils

gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

이는 다음을 반환합니다:

mv: rename ./. to ./_.: Invalid argument

그래서 find와 함께 rename을 사용해 보았습니다.

find . -type d -exec rename 's/^/_/' {} \;

이는 다음을 반환합니다:

Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory

실행 가능한 솔루션이 있거나 내가 어디에서 문제를 겪고 있는지 아는 사람이 있나요?

답변1

내가 올바르게 이해했다면 다음과 같은 파일과 디렉토리가 있습니다.

$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1  .file2  .file3  dir1  dir2  dir3  file1  file2  file3

_dir1디렉토리 이름을 , _dir2, 로 바꾸고 싶나요 _dir3?

그건 find/ printf mv가능할 것 같아. 그것이 무엇을 인쇄하는지 봅시다:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"

"점"은 특별하고 이름을 바꿀 수 없기 때문에 첫 번째 명령은 오류를 삭제합니다. 하지만 다른 것들은 괜찮을 것이고 원하는 대로 디렉토리 이름을 바꿔야 합니다:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1  _dir2  _dir3

그러나 쉘에 명령을 파이프하는 것은 약간 보기 흉하며, 파일 이름이 충분히 이상하다면 결과는 놀랄 것입니다(예: $쉘에서 변수 또는 명령 대체를 트리거하는 기호가 포함된 경우).

파일이 모두 한 수준에 있는 경우 다음을 수행해야 합니다.

for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done

_$x( 이미 존재 하는 경우 $x해당 위치로 이동됩니다.)

이름이 점으로 시작하는 디렉터리를 포함하려면 shopt -s dotglob미리 사용하십시오.

이것도 가깝습니다.

find . -type d -exec rename 's/^/_/' {} \;

그러나 로 시작하는 경로를 find제공 하므로 이를 고려해야 합니다. 한 수준에서만 이 작업을 수행해야 합니다(선행을 로 변경 ).rename./././_

find . -type d -exec rename 's,^./,./_,' {} \;

모든 수준의 디렉토리를 얻으려면 find -execdir사용하는 것이 가장 쉬울 수 있습니다. 파일 디렉터리에서 명령을 실행합니다. -depth이름 바꾸기를 올바른 순서로 처리 해야 합니다 .

find . -depth -type d -execdir rename 's,^./,./_,' {} \;

어쩌면 추가할 수도 있습니다 ! -name .. 예를 들어

$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls  _foo
_dir1  _dir2

답변2

.첫 번째 시도의 문제점은 모든 하위 디렉토리 외에도 자체 이름(현재 디렉토리)을 바꾸려고 한다는 것입니다 . 그냥 넘어가.

gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

하지만 실제로는 이렇게 하지 마세요. 절대로 물건을 sh.작동하지 않습니다.파일 이름에 쉘 특수 문자가 포함되지 않는 한. `rm -r ~`(이상하더라도 완벽하게 유효한 파일 이름) 이라는 파일이 있으면 pop이 홈 디렉토리로 이동합니다. -exec아래에 표시된 대로 쉘을 호출합니다 .

_기본 이름의 시작 부분, 즉 마지막 뒤에 추가하는 대신 전체 경로의 시작 부분에 를 삽입하기 때문에 두 번째 시도는 작동하지 않습니다 /.

find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;

find중첩된 디렉터리가 있는 경우 이들 중 어느 것도 실제로 작동하지 않습니다. 디렉터리 를 탐색하는 동안 디렉터리 이름을 바꾸기 때문입니다 . 이렇게 하면 옵션을 사용하여 디렉터리 자체에 작업을 수행하기 전에 디렉터리의 내용에 대해 작업을 수행해야 합니다 -depth.

find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;

더 이식성 있게는 쉘을 호출하고 mv. 이는 모든 POSIX 시스템에서 작동합니다.

find . -depth -type d -name . -o -exec sh -c '
    for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +

중첩된 하위 디렉터리가 없으면 간단한 셸 루프를 사용할 수 있습니다. 이름이 점으로 시작하는 디렉터리의 이름을 바꾸지 않으려면 */디렉터리 및 디렉터리에 대한 기호 링크만 일치시키는 와일드카드를 사용하면 됩니다.

for d in */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi   # robustness in case there are no subdirectories
  if [ -L "$d" ]; then continue; fi     # optional: skip symbolic links to directories
  mv -- "$d" "_${d%/}"
done

이름이 점으로 시작하는 디렉터리를 포함하여 모든 디렉터리의 이름을 바꾸려면 .*와일드카드도 포함합니다.

for d in .*/ */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
  if [ -L "$d" ]; then continue; fi
  mv -- "$d" "_${d%/}"
done

foo로 이동할 때 라는 _foo디렉토리가 이미 있는 경우 _foo이전 디렉토리 foo가 으로 이동됩니다 _foo/foo. 이를 방지하려면 명시적 테스트를 추가하세요.

또는 zsh를 사용하세요. macOS에는 사전 설치되어 있습니다. 먼저 실행하여 (이것을 스크립트에 autoload -U zmv넣음 ).zshrczmv기능, 비재귀 버전(도트 파일 포함, 기호 링크 제외)은  (/D)다음과 같은 집합입니다.글로벌 한정자):

zmv -Q '*(/D)' '_$f'

또는 재귀 버전(사용수정자dirname과 basename을 추출하려면 다음을 수행하십시오.

zmv -Q '**/*(/D)' '$f:h/_$f:t'

zmv대상이 이미 존재하는 경우 이동을 거부합니다.

관련 정보