상위 디렉터리와 하위 디렉터리의 이름이 같은 경우 파일을 상위 디렉터리로 이동합니다.

상위 디렉터리와 하위 디렉터리의 이름이 같은 경우 파일을 상위 디렉터리로 이동합니다.

동일한 이름을 가진 하위 디렉터리를 디렉터리에서 검색한 다음 하위 디렉터리의 모든 파일을 상위 디렉터리로 이동하는 방법이 필요합니다. 따라서 다음과 같은 방법을 사용하여 빈 디렉터리를 모두 삭제할 /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 

내 추측으로는 작은 Python 스크립트를 만들어 동일한 이름을 가진 하위 디렉터리가 포함된 모든 디렉터리 목록을 만들고 다음과 같은 명령을 사용하여 이 목록을 반복하는 것입니다. 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 한정자를 사용한 재귀적 globbing
  • N:nullglob: 일치하는 항목이 없어도 불평하지 마세요.
  • D: dotglob: 숨겨진 파일을 포함합니다.
  • od: 순차적 깊이 우선(잎이 자신이 있는 가지보다 먼저 옴)
  • oN: 그렇지 않으면 파일 목록을 정렬하는 데 신경 쓰지 마세요.
  • /:디렉토리 유형 파일로 제한됩니다.
  • e['expression']: expression코드가 true를 반환하는 파일을 제한합니다(현재 파일 경로가 저장되는 위치 $REPLY).
  • $REPLY:t: 파일의 꼬리(기본 이름)
  • $REPLY:h:t: 파일 헤더의 끝(디렉토리 이름))

bash4.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

findGNU 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
  • 루트 수준( )을 계산하지 않고 2개 이상의 수준에서 하위 디렉터리 경로의 모든 구성 요소에 대한 중복을 찾습니다 $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경로의 첫 번째 스푸핑된 이름 ./test1/test2/test1/test3/test2/test1/test1은 입니다 test1. 해당 내용을 동일한 이름의 가장 왼쪽 하위 디렉터리로 전송할 수 있습니다../test1/
  • 대상의 기존 파일을 삭제하지 않고 콘텐츠가 이동되면 가장 오른쪽 하위 디렉터리 수준이 test1삭제됩니다.
  • 2번째 줄을 계속 진행 tmp.data하고 위의 단계를 반복합니다.
  • 모든 행이 tmp.data소비될 때까지 기다립니다.

이 단계에서 질문(질문 작성자: @TomDerks)은 test1/*6행의 가장 오른쪽 콘텐츠로 무엇을 해야 할까요? 입니다. ~해야 한다모두해당 내용이 동일한 이름의 가장 왼쪽 디렉터리(이 경우 경로의 첫 번째 하위 디렉터리 수준)로 이동됩니까? "모두"에는 다음 파일이 포함됩니까?./test1/test2/test1/ 또한하위 디렉토리 test3와 그 내용은 무엇입니까?
완전한 솔루션(2부)은 이에 달려 있습니다.

관련 정보