하위 경로와 리프 노드(파일)를 먼저 사용하여 디렉터리를 반복적으로 나열합니까(부분 파일 이름의 일괄 이름 변경을 위해)?

하위 경로와 리프 노드(파일)를 먼저 사용하여 디렉터리를 반복적으로 나열합니까(부분 파일 이름의 일괄 이름 변경을 위해)?

중복 항목으로 선언하기 전에 파일 및 디렉터리 이름에 공통 문자열이 포함된 트리 구조의 이름을 일괄적으로 바꾸거나 새 이름으로 복사하려는 특별한 이유 때문에 이 항목이 필요하다는 점을 고려하세요. 다음은 예입니다(Ubuntu 14.04에서 시도했으므로 GNU 도구를 사용함).

cd /tmp
mkdir myproj
mkdir -p myproj/myproj_AA/myproj_BB
touch myproj/myproj_AA/myproj_BB/myproj_CC.dat
mkdir myproj/myproj_AA/myproj_DD
touch myproj/myproj_AA/myproj_DD/myproj_EE.dat
mkdir -p myproj/myproj_XX/myproj_YY
touch myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
mkdir -p myproj/myproj_XX/myproj_WW
touch myproj/myproj_XX/myproj_WW/myproj_QQ.dat
tree myproj # to visualise

디렉토리 구조는 tree다음과 같습니다.

myproj
├── myproj_AA
│   ├── myproj_BB
│   │   └── myproj_CC.dat
│   └── myproj_DD
│       └── myproj_EE.dat
└── myproj_XX
    ├── myproj_WW
    │   └── myproj_QQ.dat
    └── myproj_YY
        └── myproj_ZZ.dat

6 directories, 4 files

myproj/따라서 (자신을 포함하여 ) 모든 항목의 이름을 (이름으로 표시되는 위치마다) 대신 myproj으로 변경하고 싶습니다 . 따라서 먼저 현재 디렉터리에 대한 상대 경로가 포함된 목록을 가져와야 합니다. 그런 다음 가장 바깥쪽 하위 디렉터리(이것은 가장 긴 상대 경로 이름을 가진 파일과 같지만 확실하지 않음)가 첫 번째 디렉터리가 되도록 정렬해야 합니다. (먼저 /mv 디렉토리의 이름을 바꾼 다음 그 안에 있는 파일의 이름을 바꾸려고 하면 이전 디렉토리 이름을 첫 번째 인수로 사용하고 이제 이름이 변경되었기 때문에 실패할 수 있기 때문입니다.)myTESTprojmyproj

ls -R --group-directories-first myproj/먼저 디렉토리를 재귀적으로 사용하고 그룹화하는 방법을 알고 있지만 ls결과는 다음과 같습니다.

$ ls -R --group-directories-first myproj/
myproj/:
myproj_AA  myproj_XX

myproj/myproj_AA:
myproj_BB  myproj_DD

myproj/myproj_AA/myproj_BB:
myproj_CC.dat

myproj/myproj_AA/myproj_DD:
myproj_EE.dat

myproj/myproj_XX:
myproj_WW  myproj_YY

myproj/myproj_XX/myproj_WW:
myproj_QQ.dat

myproj/myproj_XX/myproj_YY:
myproj_ZZ.dat

...즉, 쉽게 제공할 수 있는 하위 경로가 포함된 간단한 목록이 아닙니다.while read f; do ...

내가 얻은 가장 가까운 것은 다음을 사용하는 것입니다 find.

$ find myproj/
myproj/
myproj/myproj_AA
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW
myproj/myproj_XX/myproj_WW/myproj_QQ.dat

여기에는 간단한 하위 경로 목록이 있지만 루트 노드에서 리프 노드까지 정렬되어 있으며 먼저 리프 노드를 정렬해야 합니다. 비슷한 것을 시도하고 있지만 find myproj/ | sort -n차이가 없는 것 같습니다. 그래서 내가 다음과 같이 하면:

$ find myproj/ | sort -n | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/’ -> ‘myTESTproj/’
mv: cannot stat ‘myproj/myproj_AA’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’: No such file or directory
...

...그런 다음 루트 노드(디렉토리)의 이름이 먼저 바뀌므로 예상되는 재귀 이름 바꾸기가 즉시 실패하므로 이에 대한 모든 추가 참조가 유효하지 않습니다.

그렇다면 이와 같은 일괄 이름 바꾸기에 사용하기 위해 리프 노드가 있는 하위 디렉터리의 올바른 재귀 목록을 먼저 어떻게 얻습니까?

답변1

귀하의 목표가 단지 이름을 바꾸는 것이라면 디렉토리 자체 전에 각 디렉토리의 내용을 처리하는 것만으로는 충분하지 않습니까?모두나뭇잎 (에서모두목차) 먼저? find -depth그것이 바로 그것이 하는 일입니다.

$ mkdir -p a/b c/d
$ find -depth
./a/b
./a
./c/d
./c
.

find -exec그런 다음 Bash를 사용하여 파일 이름을 바꿀 수 있습니다 .

$ find -depth ! -name . -name "*myproj*" -execdir bash -c '
    for f; do mv "$f" "${f/myproj/myTESTproj}" ; done' bash {} +

답변2

Perl 버전의 명령이 설치되어 있는 경우 이 방법이 작동합니다 rename(때때로prename

find myproj -depth -name '*myproj*' -exec rename -n 's!(.*)myproj!$1myTESTproj!' {} +

-depth옵션을 find사용하면 디렉터리 내의 하위 디렉터리가 디렉터리 자체 앞에 나열됩니다. 작업 의 접미사는 지정된 명령의 단일 호출 에 대한 다중 삽입을 +허용합니다 . 효율성이 떨어지면 로 대체할 수 있습니다 .-exec{}\;

원하는 대로 작동한다고 확신하면 제거 -n하거나 교체하세요 -v.

답변3

질문을 게시한 후 무엇을 찾아야 할지 기억해냈습니다.만약에리프 노드는 가장 긴 상대 경로 이름을 가진 노드입니다(항상 사실인지는 확실하지 않지만 적어도 OP 예에서는 그런 것 같습니다). 그러면 문자열 목록을 문자열별로 정렬하는 방법이 필요합니다. 불행히도 sort그러한 옵션은 없는 것 같습니다.

그러나 나는 발견했다https://stackoverflow.com/questions/5917576/sort-a-text-file-by-line-length-include-spaces- 그런 다음 거기에서 perl솔루션을 선택하십시오.

$ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>'
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW/myproj_QQ.dat
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_BB
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_WW
myproj/myproj_AA
myproj/myproj_XX
myproj/

그러나 sed 's/myproj/myTESTproj/g'여기서도 간단한 대체는 작동하지 않습니다.

$ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>' \
> | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ -> ‘myTESTproj/myTESTproj_AA/myTESTproj_DD/myTESTproj_EE.dat’
mv: cannot move ‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ to ‘myTESTproj/myTESTproj_AA/myTESTproj_DD/myTESTproj_EE.dat’: No such file or directory
...

...그래서 우리는sed 한 줄의 마지막 항목만 바꾸기,지금 바로 sed -E 's/(.*)myproj/\1myTESTproj/g':

$ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>' \
| while read f; do mv -v $f $(echo $f | sed -E 's/(.*)myproj/\1myTESTproj/g'); done
‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ -> ‘myproj/myproj_AA/myproj_DD/myTESTproj_EE.dat’
‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’ -> ‘myproj/myproj_AA/myproj_BB/myTESTproj_CC.dat’
‘myproj/myproj_XX/myproj_YY/myproj_ZZ.dat’ -> ‘myproj/myproj_XX/myproj_YY/myTESTproj_ZZ.dat’
‘myproj/myproj_XX/myproj_WW/myproj_QQ.dat’ -> ‘myproj/myproj_XX/myproj_WW/myTESTproj_QQ.dat’
‘myproj/myproj_AA/myproj_DD’ -> ‘myproj/myproj_AA/myTESTproj_DD’
‘myproj/myproj_AA/myproj_BB’ -> ‘myproj/myproj_AA/myTESTproj_BB’
‘myproj/myproj_XX/myproj_YY’ -> ‘myproj/myproj_XX/myTESTproj_YY’
‘myproj/myproj_XX/myproj_WW’ -> ‘myproj/myproj_XX/myTESTproj_WW’
‘myproj/myproj_AA’ -> ‘myproj/myTESTproj_AA’
‘myproj/myproj_XX’ -> ‘myproj/myTESTproj_XX’
‘myproj/’ -> ‘myTESTproj/’
$ tree myTESTproj/
myTESTproj/
├── myTESTproj_AA
│   ├── myTESTproj_BB
│   │   └── myTESTproj_CC.dat
│   └── myTESTproj_DD
│       └── myTESTproj_EE.dat
└── myTESTproj_XX
    ├── myTESTproj_WW
    │   └── myTESTproj_QQ.dat
    └── myTESTproj_YY
        └── myTESTproj_ZZ.dat

6 directories, 4 files

이것이 내 요구 사항을 충족한다고 생각합니다. 그러나 가장 긴 경로 이름 == 리프 파일 노드라는 가정이 항상 올바른지 확실하지 않으며, 설령 그렇더라도 이 작업을 수행하는 더 쉬운 방법이 있습니까?


편집: 다음과 같은 구조의 경우에는 확실히 실패합니다.

myproj/somespecdir/someotherdir/myproj_CC.dat
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/somespecdir/someotherdir
myproj/myproj_AA/myproj_DD
myproj/somespecdir
myproj/myproj_AA
myproj/

...즉, 이름이 바뀐 경로에서 검색하고 바꿀 하위 문자열의 첫 번째 항목도 마지막(고유) 항목이고 해당 하위 문자열이 여러 번 나타나는 목록의 경로 앞에 나타나는 경우입니다.

관련 정보