파일이 없는 가장 얕은 디렉터리만 나열합니다.

파일이 없는 가장 얕은 디렉터리만 나열합니다.

다음과 같은 파일 시스템 구조를 가정합니다.

ROOT
    DIR1A
        FILE
        DIR2A
        DIR2B
            DIR3A
    DIR1B
        DIR2C
        DIR2D
            DIR3B
    DIR1C
        DIR2E
            FILE

임의의 디렉터리에서 시작하여, 빈 하위 디렉터리를 나열하지 않고 a) 아무것도 포함하지 않거나 b) 빈 디렉터리만 포함하는 가장 얕은 하위 디렉터리만 나열하려면 어떻게 해야 합니까?

즉, 위의 경우 ROOT에서 시작하면 다음과 같습니다.

  1. DIR1A에는 파일이 포함되어 있으므로 나열되지 않습니다.
  2. DIR2A에는 아무것도 포함되어 있지 않으므로 나열됩니다.
  3. DIR2B에는 빈 디렉터리만 포함되어 있으므로 나열됩니다.
  4. DIR3A는 이미 나열된 더 얕은 디렉터리에 있으므로 나열되지 않습니다.
  5. DIR1B에는 빈 디렉터리만 포함되어 있으므로 나열됩니다.
  6. DIR1B의 하위 디렉터리는 이미 나열된 더 얕은 디렉터리에 있으므로 나열되지 않습니다.
  7. DIR1C나 DIR2E에는 파일이 중첩되어 있으므로 나열되지 않습니다.

나는 이것을 표현하는 더 효율적인 방법이 있다고 믿습니다. 어쩌면 "아무것도 포함하지 않거나 빈 디렉토리만 포함하는 최상위 디렉토리만 나열하고 싶습니다."?

편집: 위의 언어 중 일부를 명확히 하려고 했습니다.

답변1

디렉터리 트리를 너무 많이 탐색하지 않고 실행되는 명령 수를 최소화하려면 다음을 수행할 수 있습니다(GNU findsort유사한 NUL을 ecord 구분 기호 awk로 지원한다고 가정 ).RS

find . -type d -print0 -o -printf 'f/%h\0' |
  LC_ALL=C sort -zru |
  LC_ALL=C awk -F/ -vRS='\0' '
    function parent(path) {
      sub("/[^/]*$", "", path)
      return path
    }
    $1 == "f" {
      sep = path = ""
      for (i = 2; i <= NF; i++) {
        black[path = path sep $i]
        sep = FS
      }
      next
    }
    ! ($0 in black) && ($0 == "." || parent($0) in black)'

그 아래에 디렉토리가 아닌 파일이 있는 모든 디렉토리를 검은색으로 색칠한 다음 검은색 부모가 있는(또는 특수한 경우 부모가 없는 .) 검정색이 아닌 디렉토리를 인쇄합니다.

이러한 디렉터리를 삭제하는 것이 목표라면 다음과 같이 할 수 있습니다.

find . -depth -type d -empty -delete

-delete을 의미 -depth하지만 명확성을 위해 여기에 추가하겠습니다(GNU find매뉴얼에서 제안한 대로). -delete어떤 경우든 빈 디렉터리만 삭제되므로 -empty비어 있지 않은 디렉터리를 삭제할 수 없을 때 발생하는 오류 메시지를 방지할 수 있습니다. 깊이 우선을 사용 d하면 디렉토리가 아닌 파일을 제외한 전체 구조를 삭제하고 리프가 있는 분기 이전의 리프를 삭제하게 됩니다 .

-deleteBSD 및 GNU의 비표준 확장이지만 -empty둘 다 현재 상당히 일반적입니다. 해당 항목이 없으면 언제든지 두 개를 다음으로 대체할 수 있습니다(아마도 오류 메시지를 다음으로 삭제할 수 있음).-delete-emptyfindfind-exec rmdir {} +2> /dev/null모두find및) 오류 메시지 rmdir.

답변2

뒤늦게 이렇습니다.

find -type d -exec sh -c '[ -z "$(find "$@" -type f -print -quit)" ]' _ {} \; -print -prune

# Setup your configuration
mkdir -p root/{dir1a/{dir2a,dir2b/dir3b},dir1b/{dir2c,dir2d/dir3b},dir1c/dir2e}
touch root/{dir1a,dir1c/dir2e}/file

# Run the finder
find root -type d -exec sh -c '[ -z "$(find "$@" -type f -print -quit)" ]' _ {} \; -print -prune

# Output
root/dir1b
root/dir1a/dir2b
root/dir1a/dir2a

설명하다

하위 쉘은 각 디렉토리에 대해 차례로 호출되며 exec, 맨 위에서 시작하여 아래쪽으로(즉, 너비 우선) 작업됩니다. 현재 위치부터 파일을 검색하여 파일 true이 없으면 반환합니다. main은 find그것으로부터 상태 결과를 얻고 exec성공하면 현재 디렉토리를 인쇄하고 나머지 하위 트리 검색을 중지합니다.

관련 정보