동일한 이름을 가진 하위 디렉터리를 검색한 다음 하위 디렉터리의 모든 파일을 상위 디렉터리로 이동하는 방법이 필요합니다. 따라서 모든 빈 디렉토리를 삭제하는 것과 /recup-dir1/recup-dir1/files to /recup-dir1/files.
같은 것을 사용할 수 있기 때문에 하위 디렉토리는 비워 둘 수 있습니다.
find . -type -d -empty -delete
그래서 문제는 어떤 디렉토리에 같은 이름을 가진 하위 디렉토리가 있고 어떤 디렉토리에는 없는지 전혀 모른다는 것입니다.
의사 코드에는 이와 같은 것이 필요합니다.
While more directories are unchecked
get name-x of next dir
enter dir
If name-x/name-x exist
move all files in name-x/name-x to name-x
mark dir as done
next
내 최선의 추측은 작은 파이썬 스크립트를 만들어 같은 이름을 가진 하위 항목이 있는 모든 디렉터리의 목록을 만들고 다음과 같은 명령을 통해 이 목록을 반복하는 것입니다.
find something something -exec mv
어쩌면 이것은 bash 스크립팅으로 수행될 수도 있고 다른 솔루션이 존재할 수도 있습니다. 일부 rsync 명령과 마찬가지로, 그러나 아마도 rsync를 사용하여 이 혼란을 만들었기 때문에 그것이 해결책이 될 것이라고 생각하지 않습니다.
편집: 다음은 트리 출력의 실제 부분입니다. 최상위 디렉토리는 /mnt/external-disk/tst-backup 안에 있습니다. 하위 레벨에는 하위 디렉토리가 없습니다.
│ └── recup_dir.1
├── recup_dir.10
│ └── recup_dir.10
├── recup_dir.100
│ └── recup_dir.100
├── recup_dir.102
│ └── recup_dir.102
└── recup_dir.1020
└── recup_dir.1020
답변1
를 사용하면 zsh
다음을 수행할 수 있습니다.
for dir in **/*(NDodoN/e['[[ $REPLY:t = $REPLY:h:t ]]']); do
contents=($dir/*(NDoN))
(( $#contents == 0 )) ||
mv -- $contents $dir:h/ &&
rmdir -- $dir
done
어디:
**/*(qualifiers)
glob 한정자를 사용한 재귀적 globbingN
: nullglob: 일치하는 항목이 없어도 불평하지 마세요.D
: dotglob: 숨겨진 파일 포함od
: 깊이부터 순서를 정하세요(그들이 있는 가지보다 먼저 나옴).oN
: 그렇지 않으면 파일 목록의 순서를 정하지 않아도 됩니다./
: 디렉토리 유형의 파일로 제한됩니다.e['expression']
expression
: 코드가 true를 반환하는 파일(현재 파일 경로가 저장되어 있는 파일) 로 제한합니다$REPLY
.$REPLY:t
: 파일의 tail(기본 이름)$REPLY:h:t
: 파일 헤드(dirname)의 꼬리)
bash
4.4+ 및 GNU find
또는 대부분의 BSD를 사용하면 find
다음과 유사한 작업을 수행할 수 있습니다.
shopt -s nullglob dotglob
readarray -td '' dirs < <(
LC_ALL=C find . -depth -regex '.*\(/[^/]*\)\1' -type d -print0
)
for d in "${dirs[@]}"; do
contents=("$d"/*)
(( ${#contents[@]} == 0 )) ||
mv -- "${contents[@]}" "${d%/*}/" &&
rmdir -- "$d"
done
이번에는 정규식을 사용하여 ./path/to/dir/dir
기본 정규식 역참조를 사용하여 파일을 일치시킵니다.
답변2
find
GNU v4.8.0 및 Bash v5.1.8을 기반으로 시도해 보세요.
1부: 디렉터리 트리 구문 분석 + 하위 디렉터리 이름 중복 감지
트리의 특정 디렉터리에 다음 구조가 있다고 가정합니다.
./
|__test1/
|__dirname with space
| |__test2
| |__ test2
|__dirname **
| |__test1
|
|__reboot
| |__test1
|
|__test2/
|__test3/
|__test2/
|__test1/
|__test1/
(코드 안전성을 입증하기 위해 이상한 디렉토리 이름이 있습니다.)
일부 하위 디렉터리(하위 디렉터리)가 다른 방식으로 반복되는 것을 볼 수 있습니다. 일부는 한 번이 아닌 여러 번 반복되고(예: test1
), 하나는 반복되지 않으며( test3
) 상위 및 하위 디렉터리로 반복되거나 임의 개수의 중간 하위 디렉터리로 구분될 수 있습니다.
아래 코드는 디렉토리 구조에서 하위 디렉토리 이름이 속이는 것을 자세히 보여줍니다.
- 다음에서 시작하는 하위 디렉터리 구조에 대한 파일 트리를 구문 분석합니다.
$PWD
- 루트 수준인
$PWD
. 내 실험에서 가장 긴 하위 디렉터리 경로는 다음과 같습니다../test1/test2/test1/test3/test2/test1/test1
- 리프에서 시작하여 각 하위 디렉터리 수준에서 발견된 첫 번째 하위 디렉터리 속이기를 인쇄합니다. 즉, 하위 디렉터리 경로를 오른쪽에서 왼쪽으로 읽습니다.
- 인쇄는 역순으로 파일 방향으로 리디렉션되므로 가장 긴 하위 디렉터리 경로가 먼저 표시됩니다. 두 개의 연속 세미콜론은 경로 구성 요소(";;" 왼쪽)와 이전 글머리 기호에 따라 발견된 첫 번째 중복(";;" 오른쪽)을 구분합니다.
[암호]
$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe" ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data
$ cat -n tmp.data
1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1
파트 2: 하위 디렉터리 이름 속이는 처리; 내용 이동
에 표시된 순서대로 처리가 진행됩니다 tmp.data
.
- 의 첫 번째 줄 에서
tmp.data
경로의 첫 번째 이름 dupe는./test1/test2/test1/test3/test2/test1/test1
입니다test1
. 해당 내용을 동일한 이름을 가진 가장 왼쪽 하위 디렉터리 수준으로 전송할 수 있습니다../test1/
- 대상의 기존 파일을 방해하지 않고 내용이 이동되면 가장 오른쪽 하위 디렉터리 수준이
test1
삭제됩니다. - 의 2번째 줄로 이동하여
tmp.data
위 단계를 반복합니다. - 모든 라인이
tmp.data
소비될 때까지 등.
이 단계에서 질문(질문 작성자: @TomDerks에게)은 가장 오른쪽 test1/*
에 있는 6행을 어떻게 처리해야 합니까? 해야 한다모두해당 내용은 동일한 이름을 가진 가장 왼쪽 디렉터리로 이동됩니다. 이 경우 경로의 첫 번째 하위 디렉터리 수준은 ? "모두"에 파일이 포함됩니까?./test1/test2/test1/
게다가하위 디렉토리 test3
와 그 내용은 무엇입니까?
완전한 솔루션(2부)은 이에 달려 있습니다.