이름 시작 부분에 밑줄 "_" 문자를 포함하도록 이름을 바꿔야 하는 디렉터리 내에 여러 디렉터리가 있습니다.
테스트로서 내용에 세 개의 디렉토리가 있는 상위 디렉토리를 만들었습니다.
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
넣음 ).zshrc
zmv
기능, 비재귀 버전(도트 파일 포함, 기호 링크 제외)은 (/D)
다음과 같은 집합입니다.글로벌 한정자):
zmv -Q '*(/D)' '_$f'
또는 재귀 버전(사용수정자dirname과 basename을 추출하려면 다음을 수행하십시오.
zmv -Q '**/*(/D)' '$f:h/_$f:t'
zmv
대상이 이미 존재하는 경우 이동을 거부합니다.