상위 하위 디렉토리 이름이 같고 파일을 상위 디렉토리로 이동합니다.

상위 하위 디렉토리 이름이 같고 파일을 상위 디렉토리로 이동합니다.

동일한 이름을 가진 하위 디렉터리를 검색한 다음 하위 디렉터리의 모든 파일을 상위 디렉터리로 이동하는 방법이 필요합니다. 따라서 모든 빈 디렉토리를 삭제하는 것과 /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 한정자를 사용한 재귀적 globbing
  • N: nullglob: 일치하는 항목이 없어도 불평하지 마세요.
  • D: dotglob: 숨겨진 파일 포함
  • od: 깊이부터 순서를 정하세요(그들이 있는 가지보다 먼저 나옴).
  • oN: 그렇지 않으면 파일 목록의 순서를 정하지 않아도 됩니다.
  • /: 디렉토리 유형의 파일로 제한됩니다.
  • e['expression']expression: 코드가 true를 반환하는 파일(현재 파일 경로가 저장되어 있는 파일) 로 제한합니다 $REPLY.
  • $REPLY:t: 파일의 tail(기본 이름)
  • $REPLY:h:t: 파일 헤드(dirname)의 꼬리)

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
  • 루트 수준인 $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부)은 이에 달려 있습니다.

관련 정보