디렉토리가 비어 있는지 확인하는 방법은 무엇입니까?

디렉토리가 비어 있는지 확인하는 방법은 무엇입니까?

./123예를 들어 빈 경로 매개 변수를 사용하여 스크립트를 실행하는 경우 요구 사항이 있습니다 /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions(이 디렉터리는 비어 있음). "디렉토리가 비어 있습니다"라고 표시되어야 합니다.

내 코드는 다음과 같습니다

#!/bin/bash
dir="$1"

if [ $# -ne 1 ]
then
    echo "please pass arguments" 
exit
fi

if [ -e $dir ]
then
printf "minimum file size: %s\n\t%s\n" \
 $(du $dir -hab | sort -n -r | tail -1)

printf "maximum file size: %s\n\t%s\n" \
 $(du $dir -ab | sort -n | tail -1)

printf "average file size: %s"
du $dir -sk | awk '{s+=$1}END{print s/NR}'
else
   echo " directory doesn't exists"
fi

if [ -d "ls -A $dir" ]
 then
    echo " directory is  empty"
fi

./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions스크립트 이름(이 디렉터리는 비어 있음)을 실행하면 오류가 표시됩니다.

minimum file size: 4096
    /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
maximum file size: 4096
    /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
average file size: 4

"디렉토리가 비어 있습니다"라는 출력을 표시하는 대신 위의 출력을 표시합니다.

올바른 매개변수(올바른 디렉터리 경로를 의미함)를 사용하여 스크립트를 실행하면 다음 출력이 표시되어야 합니다. 설명하다./123 /usr/share

minimum file size: 196
        /usr/share
    maximum file size: 14096
        /usr/share
    average file size: 4000

내 예상 결과는 다음과 같습니다./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions

directory is empty.

답변1

if    ls -A1q ./somedir/ | grep -q .
then  ! echo somedir is not empty
else  echo somedir is empty
fi

위의 내용은 POSIX 호환 테스트이며 매우 빠릅니다. 각 파일/디렉터리의 각 줄(from ) 을 제외한 ls디렉터리의 모든 파일/디렉터리를 나열 하고 인쇄할 수 없는 모든 문자를 인용 합니다....-A-1-q( \n엘라인 포함)출력에 물음표가 있습니다 ?. 이렇게 하면 grep입력에 단일 문자가 수신되면 true를 반환하고, 그렇지 않으면 false를 반환합니다 .

POSIX-shell에서만 이 작업을 수행하십시오.

cd  ./somedir/ || exit
set ./* ./.[!.]* ./..?*
if   [ -n "$4" ] ||
     for e do 
         [ -L "$e" ] ||
         [ -e "$e" ] && break
     done
then ! echo somedir is not empty
else   echo somedir is empty
fi
cd "$OLDPWD"

POSIX 쉘( -f이전에는 파일 이름 생성이 비활성화되지 않았습니다)위의 명령 다음에 위치 인수 set배열을 리터럴 문자열로 변환 하거나 각 명령의 끝에서 glob 연산자에 의해 생성된 필드로 변환합니다. 이것이 수행되는지 여부는 glob이 실제로 무엇과 일치하는지 여부에 따라 달라집니다. 일부 셸에서는 구문 분석되지 않은 glob에 null로 확장하거나 전혀 확장하지 않도록 지시할 수 있습니다. 이는 때로는 유익하지만 이식성이 없으며 특수한 셸 옵션을 설정한 다음 나중에 설정을 해제해야 하는 등의 다른 문제를 야기하는 경우가 많습니다."$@"set

Null 인수를 처리하는 유일한 이식 가능한 방법은 비어 있거나 설정되지 않은 변수 또는 ~물결표 확장을 포함합니다. 그런데 후자가 전자보다 훨씬 안전합니다.

-e위의 셸은 지정된 세 개의 glob이 모두 여러 일치 항목으로 확인되는 경우 파일이 존재하는지 여부만 테스트합니다. 따라서 for루프는 3회 이하의 반복 동안만 실행되며, 빈 디렉터리의 경우 또는 하나 이상의 패턴이 단일 파일로만 확인되는 경우에만 실행됩니다. glob 중 하나라도 실제 파일을 나타내는 경우 s for이기도 합니다 break. 가능성이 가장 높은 것부터 가능성이 가장 낮은 것까지 glob을 주문했기 때문에 거의 매번 첫 번째 반복에서 종료됩니다.

어느 쪽이든 관련 시스템 호출은 하나만 있어야 합니다 stat()(셸). 디렉터리를 쿼리하고 해당 파일을 나열하는 ls데만 필요합니다.stat()디렉토리 항목보고서가 포함되어 있습니다. 이는 나열할 수 있는 모든 파일의 동작 find과 완전히 대조됩니다.stat()

답변2

GNU 또는 최신 BSD를 사용하면 find다음을 수행할 수 있습니다.

if find -- "$dir" -prune -type d -empty | grep -q '^'; then
  printf '%s\n' "$dir is an empty directory"
else
  printf >&2 '%s\n' "$dir is not empty, or is not a directory" \
                    "or is not readable or searchable in which case" \
                    "you should have seen an error message from find above."
fi

( 등과 $dir같은 술어처럼 보이지 않는다고 가정합니다 find.)!(-name...

POSIX적으로:

if [ -d "$dir" ] && files=$(ls -qAH -- "$dir") && [ -z "$files" ]; then
  printf '%s\n' "$dir is an empty directory"
else
  printf >&2 '%s\n' "$dir is not empty, or is not a directory" \
                    "or is not readable or searchable in which case" \
                    "you should have seen an error message from ls above."
fi

그 사람은 그것이 $dir디렉토리인지 확인합니다뒤쪽에심볼릭 링크 해결.

답변3

[-z $dir ][-z대부분의 시스템에서 명령이 호출되지 않는다고 불평합니다.괄호 주위에는 공백이 필요합니다..

[ -z $dir ]dir이는 대부분의 다른 값에 대해 비어 있고 false인 경우 참이지만, 예를 들어 dir값이 is 또는 인 경우에는 참입니다.dir= -z-o -o -n -n명령 대체에는 항상 큰따옴표를 사용하십시오.(이것은 스크립트의 나머지 부분에도 적용됩니다.)

[ -z "$dir" ]변수 값이 dir비어 있는지 테스트합니다. 변수의 값은 문자열이며, 이는 디렉토리 경로입니다. 이는 디렉토리 자체에 대해서는 아무 것도 알려주지 않습니다.

일반 파일처럼 디렉토리가 비어 있는지 테스트하는 연산자가 없습니다( [ -s "$dir" ]디렉토리가 비어 있더라도 마찬가지입니다). 디렉토리가 비어 있는지 테스트하는 간단한 방법은 해당 내용을 나열하는 것입니다. 빈 텍스트가 나타나면 디렉토리가 비어 있는 것입니다.

if [ -z "$(ls -A -- "$dir")" ]; then
  ...
fi

가 없는 이전 시스템에서는 and 를 ls -A사용할 수 있습니다 .ls -a...


if [ -z "$(LC_ALL=C ls -a -- "$dir")" = "$(printf '.\n..')" ]; then
...
fi

답변4

내 tldr 답변은 다음과 같습니다.

function emptydir {
 [ "$1/"* "" = "" ]  2> /dev/null &&
 [ "$1/"..?* "" = "" ]  2> /dev/null &&
 [ "$1/".[^.]* "" = "" ]  2> /dev/null ||
 [ "$1/"* = "$1/*" ]  2> /dev/null && [ ! -e "$1/*" ] &&
 [ "$1/".[^.]* = "$1/.[^.]*" ]  2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
 [ "$1/"..?* = "$1/..?*" ]  2> /dev/null && [ ! -e "$1/..?*" ]
}

POSIX와 호환되며 중요하지 않습니다. 일반적으로 디렉토리를 나열하고 출력을 grep으로 파이프하는 솔루션보다 빠릅니다.

용법:

if emptydir adir
then
  echo "nothing found" 
else 
  echo "not empty" 
fi

나는 이 대답을 좋아한다https://unix.stackexchange.com/a/202276/160204, 다음과 같이 다시 작성합니다.

function emptydir {
  ! { ls -1qA "./$1/" | grep -q . ; }
}

디렉토리를 나열하고 결과를 grep으로 파이프합니다. 대신 전역 확장 및 비교를 기반으로 하는 간단한 함수를 생각해 냈습니다.

function emptydir {   
 [ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}

이 함수는 표준 POSIX가 아니며 $().

설명하다:

확장이 일어나지 않을 때는 왼쪽(LHS)이 비어 있는데, 이는 디렉토리가 비어 있을 때 발생합니다. nullglob 옵션이 필요합니다. 그렇지 않으면 일치하는 항목이 없을 때 glob 자체가 확장의 결과가 되기 때문입니다. (디렉토리가 비어 있으면 RHS가 LHS의 glob과 일치하는 것이 작동하지 않습니다. LHS glob이 glob 자체라는 단일 파일과 일치할 때 거짓 긍정이 발생하기 때문입니다. *파일 이름에 있는 glob의 하위 문자열이 일치합니다. *) 중괄호 표현식은 {,.[^.],..?}다음을 포함합니다 . 숨겨진 파일은 있지만 ..또는 ..

shopt -s nullglob(하위 쉘) 내부에서 실행되기 때문에 현재 쉘의 옵션을 $()변경하지 않으며 nullglob이는 일반적으로 좋은 것입니다. 반면, 일치하는 항목이 없을 때 무언가를 반환하는 glob에서 오류가 발생하기 쉽기 때문에 스크립트에서 이 옵션을 설정하는 것이 좋습니다. 따라서 nullglob 옵션은 스크립트 시작 부분에 설정할 수 있으며 함수에서는 필요하지 않습니다. 이것을 기억하자: 우리는 nullglob 옵션과 함께 작동하는 솔루션이 필요합니다.

지침:

디렉토리에 대한 읽기 액세스 권한이 없으면 함수는 마치 빈 디렉토리가 존재하는 것처럼 동일한 정보를 보고합니다. 이는 디렉터리를 나열하고 출력을 위해 grep을 수행하는 함수에도 적용됩니다.

shopt -s nullglob명령은 표준 POSIX가 아닙니다.

에 의해 사용됩니다 $(). 큰 문제는 아니지만 피할 수 있다면 좋을 것입니다.

전문적인:

중요하지는 않지만 이 함수는 프로세스 내 커널에 소비된 CPU 시간으로 측정했을 때 이전 함수보다 4배 빠릅니다.

기타 솔루션:

LHS에서 POSIX가 아닌 명령을 제거 하고 RHS에 문자열을 넣은 다음 이름이 shopt -s nullglob지정된 파일만 있거나 디렉터리에 있는 경우 "$1/* $1/.[^.]* $1/..?*"발생하는 오탐지를 개별적으로 제거할 수 있습니다.'*'.[^.]*..?*

function emptydir {
 [ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
 [ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}

shopt -s nullglob명령이 없으면 지금 하위 쉘을 제거하는 것이 합리적이지만 단어 분리를 피하고 LHS에서 전역 확장을 허용하므로 주의해야 합니다. 특히 토큰화를 피하기 위한 인용은 글로벌 확장을 방해하므로 작동하지 않습니다. 우리의 해결책은 글로브를 개별적으로 고려하는 것입니다.

function emptydir {
 [ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
 [ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
 [ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}

여전히 개별 glob을 토큰화하지만 이제는 디렉터리가 비어 있지 않은 경우에만 오류가 발생하므로 괜찮습니다. LHS에 특정 glob과 일치하는 파일이 많은 경우 오류 메시지를 삭제하기 위해 2> /dev/null을 추가했습니다.

우리는 nullglob 옵션과도 작동하는 솔루션을 원한다는 것을 기억합니다. 위의 솔루션은 nullglob 옵션을 사용하면 실패합니다. 디렉터리가 비어 있으면 LHS도 비어 있기 때문입니다. 운 좋게도 디렉토리가 비어 있지 않을 때 디렉토리가 비어 있다고 말하지 않습니다. 비어 있다고 해서 비어 있다고는 할 수 없습니다. 따라서 nullglob 옵션을 독립적으로 관리할 수 있습니다. 구문적으로 잘못된 [ "$1/"* = "" ]etc로 확장되기 때문에 단순히 대소문자 등을 추가할 수 없습니다 . [ = "" ]그래서 우리는 [ "$1/"* "" = "" ]대신 etc.를 사용합니다. and *가 숨겨진 파일과 일치하지만 and 가 일치하지 않는 세 가지 경우를 다시 고려해야 합니다 . nullglob 옵션이 없다면 비어 있지 않을 때 비어 있다고 말하지 않기 때문에 방해가 되지 않습니다. 따라서 최종 제안된 솔루션은 다음과 같습니다. ..?*.[^.]*...

function emptydir {
 [ "$1/"* "" = "" ]  2> /dev/null &&
 [ "$1/"..?* "" = "" ]  2> /dev/null &&
 [ "$1/".[^.]* "" = "" ]  2> /dev/null ||
 [ "$1/"* = "$1/*" ]  2> /dev/null && [ ! -e "$1/*" ] &&
 [ "$1/".[^.]* = "$1/.[^.]*" ]  2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
 [ "$1/"..?* = "$1/..?*" ]  2> /dev/null && [ ! -e "$1/..?*" ]
}

보안 질문:

빈 디렉터리에 두 개의 파일을 만들고 rm프롬프트에서 실행합니다. glob은 으로 확장되며 삭제를 위해 이 작업이 수행됩니다. 우리 함수에서 glob이 있는 확장은 명령으로 처리되지 않고 에서처럼 매개변수로 처리되기 때문에 이것은 보안 문제가 아닙니다.x**rm xxfor f in *

관련 정보