디렉토리가 비어 있는지 확인하는 방법


15

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


누군가 내 코드를 한 번 확인하십시오. 나는 그것을 편집
부처님 스리 칸스

답변:


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

위의 내용은 POSIX 호환 테스트이며 매우 빠릅니다. ls디렉토리를 제외한 모든 파일 / 디렉토리를 한 줄에 하나씩 제외 .하고 출력 할 수없는 모든 문자 ( ewline 포함 ) 를 물음표 와 함께 출력 합니다. 이 방법으로 입력에서 단일 문자를 수신 하면 true를 반환하고 그렇지 않으면 false를 반환합니다...-q\n?grep

POSIX 쉘에서만 수행하려면 다음을 수행하십시오.

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 표준 쉘 (이전되지 중지됨 -filename 생성) 것 의해 뒤에 문자 스트링 중 하나에 위치 파라미터 어레이 위 또는 다른 각각의 단부에 글로브 운영자에 의해 발생 된 필드에 명령. 그렇게하는지 여부는 glob가 실제로 어떤 것과 일치하는지에 달려 있습니다. 일부 쉘에서는 비분 해 글로브를 널로 확장하도록 지시하거나 전혀 아무것도 지정하지 않을 수 있습니다. 이것은 때때로 유리할 수 있지만, 이식성이 없으며 특별한 쉘 옵션을 설정하고 나중에 설정을 해제해야하는 등의 추가적인 문제가 있습니다.set"$@"set

널값 인수를 처리 할 수있는 유일한 방법은 비어 있거나 설정되지 않은 변수 또는 ~물결표 확장을 포함합니다. 그리고 후자는 전자보다 훨씬 안전합니다.

위에서 -e세 개의 glob 중 어느 것도 둘 이상의 일치 항목으로 해석되지 않는 경우 쉘은 파일 중 하나에 대해서만 xistence를 테스트합니다 . 따라서 for루프는 3 회 이하의 반복 동안, 그리고 빈 디렉토리의 경우 또는 하나 이상의 패턴이 단일 파일로만 해석되는 경우에만 실행됩니다. 는 for또한 breakglobs와의 실제 파일을 나타내는 경우에요 - 나는 것 이상으로 가능성이 가장의 순서로 globs의 배열대로, 그것은 거의 첫 번째 반복에 모든 시간을 종료해야합니다.

어떤 방법을 사용하든 stat()쉘 은 단일 시스템 호출 만 포함해야 합니다. 쉘 lsstat()쿼리 한 디렉토리 에만 필요하고 해당 dentries 가 포함 하는 파일을 나열 해야합니다. 이것은 목록에있는 모든 파일을 find대신 하는 동작과 대조됩니다 stat().


확인할 수 있습니다. 필자가 작성한 스크립트에서 mikeserv의 초기 예제를 사용합니다.
Otheus

존재하지 않거나 읽기 권한이없는 빈 디렉토리로보고됩니다. 두 번째로, 읽기 액세스 권한은 있지만 검색 액세스 권한이없는 경우 YMMV.
Stéphane Chazelas 12

@ StéphaneChazelas-아마도 이러한 보고서에는 사용자에게이를 알리는 오류 메시지가 표시됩니다.
mikeserv

1
ls경우 에만 . [그들이 접근 할 수 없을 때 움켜 쥐고 침묵합니다. 예를 들어, [ -e /var/spool/cron/crontabs/stephane ]해당 이름의 파일이있는 동안 자동으로 false를보고합니다.
Stéphane Chazelas

somedir이 디렉토리 (또는 디렉토리가 아닌)에 대한 심볼릭 링크 인 경우 이러한 솔루션은 동일하지 않습니다.
Stéphane Chazelas

6

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

POSIXly :

if [ -d "$dir" ] && files=$(ls -qAL -- "$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

4

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

[ -z $dir ]dir는 비어 있으면 true이고 대부분의 다른 값에 대해서는 false dir이지만 신뢰할 수 없습니다. 예를 들어 값 dir= -z또는 인 경우 true -o -o -n -n입니다. 명령 대체에 항상 큰 따옴표를 사용하십시오 (이는 스크립트의 나머지 부분에도 적용됨).

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

일반 파일의 경우와 같이 디렉토리가 비어 있는지 테스트 할 연산자는 없습니다 ( [ -s "$dir" ]비어 있어도 디렉토리에 해당). 디렉토리가 비어 있는지 테스트하는 간단한 방법은 컨텐츠를 나열하는 것입니다. 빈 텍스트가 있으면 디렉토리가 비어 있습니다.

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

하지 않는 구형 시스템에서 ls -A, 당신이 사용할 수 ls -a있지만 다음 ...같습니다.

if [ -z "$(LC_ALL=C ls -a -- "$dir")" = ".
.." ]; then 

( ..문자열은 줄 바꿈과 여분의 공백이 아닌 줄 바꿈 만 포함해야하므로 시작하는 줄을 들여 쓰지 마십시오 .)


이 부분 "$ (ls -A-"$ dir ")"을 설명해 주시겠습니까? 대괄호 안팎의 $는 무엇을합니까?
부처님 sreekanth

@buddhasreekanth $(…)명령 대체
Gilles 'SO-stop

@Giles "$ (ls -A-"$ dir ")"에서 던지는 오류가 발생하지 않습니다.
부처님 sreekanth

@buddhasreekanth 오류가 무엇인가요? 복사 붙여 넣기. 관찰 한 내용을 정확하게 설명하지 않고 "작동하지 않는다"고 말하면 사람들이 도움을 줄 수 없습니다.
Gilles 'SO- 악의를 멈추십시오'

@Giles 이것은 오류입니다. 내 스크립트 ./filestats를 실행하면 오류가 발생합니다. $ / filestats / home / sreekanth / testdir / aki / schatz 최소 파일 크기 : 4096 / home / sreekanth / testdir / aki / schatz 최대 파일 크기 : 4096 / home / sreekanth / testdir / aki / schatz 평균 파일 크기 : 4 디렉토리가 비어 있습니다. "directory is empty"를 표시하는 대신 최소 크기, 최대 크기 및 평균 크기로 표시됩니다.
부처님 sreekanth

3

디렉토리 자체의 속성을보고 있습니다.

$ mkdir /tmp/foo
$ ls -ld /tmp/foo
drwxr-xr-x 2 jackman jackman 4096 May  8 11:32 /tmp/foo
# ...........................^^^^

몇 개의 파일이 있는지 계산하려고합니다.

$ dir=/tmp/foo
$ shopt -s nullglob
$ files=( "$dir"/* "$dir"/.* )
$ echo ${#files[@]}
2
$ printf "%s\n" "${files[@]}"
/tmp/foo/.
/tmp/foo/..

따라서 "directory is empty"에 대한 테스트는 다음과 같습니다.

function is_empty {
    local dir="$1"
    shopt -s nullglob
    local files=( "$dir"/* "$dir"/.* )
    [[ ${#files[@]} -eq 2 ]]
}

이처럼 :

$ if is_empty /tmp/foo; then echo "it's empty"; else echo "not empty"; fi
it's empty
$ touch /tmp/foo/afile
$ if is_empty /tmp/foo; then echo "it's empty"; else echo "not empty"; fi
not empty

디렉토리에 대한 읽기 권한이 없거나 존재하지 않는 경우 디렉토리가 비어 있다고보고합니다.
Stéphane Chazelas

1

내 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으로 파이프합니다. 대신, glob 확장 및 비교에 기반한 간단한 함수를 제안합니다.

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

이 함수는 표준 POSIX가 아니며을 사용하여 서브 쉘을 호출합니다 $(). 이 간단한 기능을 먼저 설명하여 나중에 최종 솔루션을 더 잘 이해할 수 있습니다 (위의 tldr 답변 참조).

설명:

확장이 발생하지 않으면 왼쪽 (LHS)이 비어 있습니다. 디렉토리가 비어있는 경우입니다. 일치하지 않는 경우 glob 자체가 확장의 결과이므로 nullglob 옵션이 필요합니다. (디렉토리가 비어있을 때 RHS가 LHS의 glob와 일치하는 것은 LHS glob가 glob 자체로 명명 된 단일 파일과 일치 할 때 발생하는 잘못된 긍정으로 인해 작동하지 않습니다. glob 의 파일 이름 *의 하위 문자열과 일치합니다 *. ) 중괄호 표현식 {,.[^.],..?}은 숨겨진 파일을 포함하지만 ..또는 은 포함하지 않습니다 ..

(서브 쉘) shopt -s nullglob내부에서 실행 되기 때문에 현재 쉘 $()nullglob옵션을 변경하지 않습니다 . 일반적으로 좋은 것입니다. 반면에 스크립트에서이 옵션을 설정하는 것이 좋습니다. 일치하지 않으면 glob이 무언가를 반환하는 경향이 있기 때문입니다. 따라서 스크립트 시작시 nullglob 옵션을 설정할 수 있으며이 기능에는 필요하지 않습니다. 이것을 명심하자 : 우리는 nullglob 옵션과 함께 작동하는 솔루션을 원한다.

주의 사항 :

디렉토리에 대한 읽기 권한이없는 경우, 함수는 빈 디렉토리가있는 것과 동일하게보고합니다. 이것은 디렉토리를 나열하고 출력을 grep하는 기능에도 적용됩니다.

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

로 작성된 서브 쉘을 사용합니다 $(). 큰 문제는 아니지만 피할 수 있다면 좋습니다.

찬성:

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

다른 솔루션 :

shopt -s nullglobLHS에서 POSIX가 아닌 명령을 제거하고 문자열 "$1/* $1/.[^.]* $1/..?*"을 RHS에 넣을 수 있으며 이름이 '*', .[^.]*또는 ..?*디렉토리에 파일 만있을 때 발생하는 오 탐지를 별도로 제거 할 수 있습니다 .

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

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

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

우리는 여전히 개별 glob에 대해 단어 분할을 가지고 있지만 디렉토리가 비어 있지 않을 때만 오류가 발생하기 때문에 지금은 괜찮습니다. 우리는 2> / dev / null을 추가하여 LHS에 주어진 glob과 일치하는 많은 파일이있을 때 오류 메시지를 삭제했습니다.

nullglob 옵션과 함께 작동하는 솔루션을 원한다는 것을 기억합니다. 디렉토리가 비어 있으면 LHS도 비어 있으므로 위의 솔루션은 nullglob 옵션으로 실패합니다. 다행히도 디렉토리가 비어 있지 않다면 절대로 말하지 않습니다. 그것이 비어있을 때만 말하는 것은 아닙니다. 따라서 nullglob 옵션을 별도로 관리 할 수 ​​있습니다. 문법적으로 잘못된 등으로 [ "$1/"* = "" ]확장되므로 사례 등을 추가 할 수 없습니다 [ = "" ]. 그래서 우리는 [ "$1/"* "" = "" ]대신 등을 사용합니다. 우리는 다시 세 가지 사례를 고려해야합니다 *, ..?*그리고 .[^.]*숨겨진 파일과 일치 할 수 있지만 .... 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하고 x빈 디렉토리 및 실행 *프롬프트에. glob *가 확장 rm x되고이를 제거하기 위해 실행됩니다 x. 우리의 함수에서 globs는 확장이 명령으로 보이지 않고 에서처럼 인수로 보이는 곳에 위치하기 때문에 이것은 보안상의 문제가 아닙니다 for f in *.


$(set -f; echo "$1"/*)다소 복잡한 글쓰기 방법 인 것 같습니다 "$1/*". 그리고 이것은 숨겨진 디렉토리 또는 파일과 일치하지 않습니다.
muru December

내가 의미하는 것은에는 파일 이름 확장이 없다는 것입니다 "$1/*"(따옴표 포함주의 *소위), set -f및 서브 쉘이 불필요하다 특정한다.
muru December

디렉토리에 숨겨진 파일이 아닌지 또는 디렉토리가 있는지 여부에 대한 다른 질문에 대답합니다. 나는 그것에 대해 죄송합니다. 기꺼이 삭제하겠습니다.
Dominic108

1
너무 숨겨진 파일과 일치 (의 복잡 글로브 볼 수있는 방법이 있습니다 mikeserv의 대답은 : ./* ./.[!.]* ./..?*). 어쩌면 그 기능을 통합하여 기능을 완성시킬 수도 있습니다.
muru December

아! 나는 이것을보고 배우고 어쩌면 우리는 globbing 확장을 기반으로 완전한 대답을 가질 것입니다!
Dominic108

0

여기 또 다른 간단한 방법이 있습니다. D가 공허함을 테스트하려는 디렉토리의 전체 경로 이름이라고 가정하십시오.

그때

if [[ $( du -s  D |awk ' {print $1}') = 0 ]]
then
      echo D is empty
fi

이것은 작동하기 때문에

du -s D

출력으로

size_of_D   D

awk는 D를 제거하고 크기가 0이면 디렉토리가 비어 있습니다.


-1
#/bin/sh

somedir=somedir
list="`echo "$somedir"/*`"

test "$list" = "$somedir/*" && echo "$somedir is empty"

# dot prefixed entries would be skipped though, you have to explicitly check them

2
안녕하세요. 사이트에 오신 것을 환영합니다. 코드 만 있고 설명이없는 답변은 게시하지 마십시오. 코드에 주석을 달거나 수행중인 작업을 설명하십시오. 그런데, 사용에 이유가 없다 echo, 당신이 필요로하는 모든입니다 list="$somedir/*". 또한 이것은 까다 롭고 오류가 발생하기 쉽습니다. 예를 들어 OP는 뒤에 슬래시를 포함하여 대상 디렉토리의 경로를 제공해야한다고 언급하지 않았습니다.
terdon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.