Skrypty Bash: test na pusty katalog

95

Chcę sprawdzić, czy katalog nie zawiera żadnych plików. Jeśli tak, pominę trochę przetwarzania.

Próbowałem następujące:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Daje to następujący błąd:

line 1: [: too many arguments

Czy istnieje rozwiązanie / alternatywa?

Anthony Kong
źródło
powiązane: stackoverflow.com/q/91368/52074
Trevor Boyd Smith

Odpowiedzi:

120
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

Byłoby również fajnie sprawdzić, czy katalog istnieje wcześniej.

Andrey Atapin
źródło
11
Nie używaj &&i ||jednocześnie! Jeśli echo "Not Empty"zawiedzie, echo "Empty"uruchomi się! Spróbuj echo "test" && false || echo "fail"! Tak, wiem, echoże nie zawiedzie, ale jeśli zmienisz jakiekolwiek inne polecenie, będziesz zaskoczony!
uzsolt
4
Podaj co najmniej jeden przykład, gdy powyższy kod nie działa. Konkretnie ten kod jest absolutnie poprawny. Mam nadzieję, że pytający jest w stanie dostosować to do własnych celów
Andrey Atapin
3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- jeśli chcesz „oznaczyć” niepuste katalogi utworzonym plikiem o nazwie non-empty, zawiedzie i wyświetli „Pusty”.
uzsolt
4
gdzie jest touch /emptymoja linia?
Andrey Atapin
7
O nie! Istnieje bardzo ważny problem z tym kodem. Powinno być if [ -n "$(ls -A /path/to/dir)" ]; then... Zaktualizuj odpowiedź, zanim ktoś wklei ten kod na swój serwer, a haker wymyśli, jak go wykorzystać. Jeśli /path/to/dirnie jest pusty, wówczas nazwy plików są przekazywane jako argumenty, do /bin/testktórych wyraźnie nie jest przeznaczony. Wystarczy dodać -nargument, problem rozwiązany. Dzięki!
Edward Ned Harvey,
21

Nie ma potrzeby liczenia czegokolwiek ani globusów powłoki. Możesz także używać readw połączeniu z find. Jeśli finddane wyjściowe są puste, zwrócisz false:

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

To powinno być przenośne.

slhck
źródło
Dobre rozwiązanie, ale myślę, że twoje echopołączenia odzwierciedlają zły wynik: w moim teście (pod Cygwinem) find . -mindepth 1 | readmiałem 141 kod błędu w niepustym katalogu, a 0 w pustym katalogu
Lucas Cimon
@LucasCimon Nie tutaj (macOS i GNU / Linux). Dla niepustego katalogu readzwraca 0, a dla pustego 1.
slhck
1
PSA nie działa zset -o pipefail
Pułkownikiem Trzydzieści Dwa
19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi
uzsolt
źródło
Prawidłowo i szybko. Miły!
l0b0
4
Potrzebuje cudzysłowów (2x), a test -njest poprawny i bezpieczny (przetestuj w katalogu ze spacjami w nazwie, przetestuj w niepustym katalogu o nazwie „0 = 1”). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin
1
@ivan_pozdeev To nieprawda, przynajmniej w przypadku GNU find. Być może myślisz grep. serverfault.com/questions/225798
Vladimir Panteleev
Pisanie może być prostsze find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .i polegać na statusie wyjścia z grep. Niezależnie od tego, jak to zrobisz, jest to odpowiednia odpowiedź na to pytanie.
Tom Anderson
13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi
Renaud
źródło
4
To musi być to, nie ma potrzeby analizowania wyjścia ls, po prostu sprawdź, czy jest pusty, czy nie. Korzystanie z funkcji wyszukiwania wydaje mi się przesadą.
akostadinov
4

Spowoduje to wykonanie zadania w bieżącym katalogu roboczym (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

lub to samo polecenie podzielone na trzy linie, aby było bardziej czytelne:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Wystarczy wymienić ls -1A . | wc -lze ls -1A <target-directory> | wc -ljeśli trzeba uruchomić go na innym folderze docelowym.

Edit : Wymieniłem -1az -1A(patrz @Daniel komentarz)

ztank1013
źródło
2
użyj ls -Azamiast tego. Niektóre systemy plików nie posiada .i ..dowiązania symboliczne według docs.
Daniel Beck
1
Dzięki @Daniel, zredagowałem odpowiedź po Twojej sugestii. Wiem, że „1” też może zostać usunięte.
ztank1013,
2
Nie boli, ale sugeruje się, że nie wyprowadza go na terminal. Ponieważ potokujesz go do innego programu, jest zbędny.
Daniel Beck
-1jest zdecydowanie zbędny. Nawet jeśli lsnie wydrukuje jednego elementu w wierszu, gdy zostanie potokowany, nie ma to wpływu na pomysł sprawdzenia, czy wygenerował zero lub więcej wierszy.
Victor Yarema
3

Użyj następujących opcji:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

W ten sposób jesteś niezależny od formatu wyjściowego ls. -mindepthpomija sam katalog, -maxdepthzapobiega rekurencyjnej obronie w podkatalogach w celu przyspieszenia.

Daniel Beck
źródło
Oczywiście, jesteś teraz zależne wc -li findformat (co jest dość prosty choć).
Daniel Beck
3

Za pomocą tablicy:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi
Glenn Jackman
źródło
3
Bardzo fajne rozwiązanie, ale pamiętaj, że wymagashopt -s nullglob
xebeche
3
${#files[@]} == 2Założenie nie wytrzymuje do głównego katalogu (prawdopodobnie nie będzie sprawdzić, czy jest ona pusta, ale niektóre kod, który nie wie o tym ograniczeniem może).
ivan_pozdeev
3

Hacky, ale tylko bash, wolny od PID sposób:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

Wykorzystuje to fakt, że testwbudowane wyjście kończy się na 2, jeśli podano więcej niż jeden argument po -e: Po pierwsze, "$1"/*glob jest rozwijany przez bash. Daje to jeden argument na plik. Więc

  • Jeśli nie ma plików, gwiazdka w test -e "$1"*nie rozwija się, więc Shell wraca do próby pliku o nazwie *, która zwraca 1.

  • ... z wyjątkiem tego, że tak naprawdę istnieje jeden plik o nazwie dokładnie *, to gwiazdka rozwija się do well, gwiazdka, która kończy się tak samo jak powyżej, tj. test -e "dir/*", właśnie ten czas zwraca 0. (Dzięki @TrueY za zwrócenie na to uwagi.)

  • Jeśli jest jeden plik, test -e "dir/file"uruchamiany jest, który zwraca 0.

  • Ale jeśli jest więcej plików niż 1, test -e "dir/file1" "dir/file2" uruchamiany jest bash, który zgłasza to jako błąd użycia, tj. 2.

case otacza całą logikę, dzięki czemu tylko pierwszy przypadek z 1 statusem wyjścia jest zgłaszany jako sukces.

Możliwe problemy, których nie sprawdziłem:

  • Jest więcej plików niż liczba dozwolonych argumentów - myślę, że może to zachowywać się podobnie jak w przypadku plików 2+.

  • Albo istnieje plik z pustą nazwą - nie jestem pewien, czy jest to możliwe na żadnym rozsądnym OS / FS.

Alois Mahdal
źródło
1
Drobna korekta: jeśli nie ma pliku w katalogu /, to test -e dir/*jest wywoływany. Jeśli jedynym plikiem jest „*” w katalogu, test zwróci 0. Jeśli jest więcej plików, wówczas zwraca 2. Więc działa tak, jak opisano.
TrueY
Masz rację, @TrueY, włączyłem to do odpowiedzi. Dzięki!
Alois Mahdal,
2

Z FIND (1) (w Linuksie i FreeBSD) możesz nierekurencyjnie patrzeć na pozycję katalogu przez „-maxdepth 0” i sprawdzić, czy jest pusta za pomocą „-empty”. W odniesieniu do pytania daje to:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi
TimJ
źródło
Może nie być w 100% przenośny, ale jest elegancki.
Craig Ringer
1

Myślę, że najlepszym rozwiązaniem jest:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

dzięki https://stackoverflow.com/a/91558/520567

To jest anonimowa edycja mojej odpowiedzi, która może, ale nie musi być pomocna dla kogoś: niewielka zmiana daje liczbę plików:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

Działa to poprawnie, nawet jeśli nazwy plików zawierają spacje.

akostadinov
źródło
1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDYCJA: Myślę, że to rozwiązanie działa dobrze z gnu find, po szybkim spojrzeniu na implementację . Ale to może nie działać na przykład z znalezieniem netbsd . Rzeczywiście, ten używa pola st_size stat (2). Podręcznik opisuje to jako:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

Lepszym rozwiązaniem, również prostszym, jest:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Również -prune w 1. rozwiązaniu jest bezużyteczne.

EDYCJA: nie -exit dla gnu find .. powyższe rozwiązanie jest dobre dla znalezienia NetBSD. W przypadku wyszukiwania GNU powinno to działać:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi
Yarl
źródło
find z GNU findutils 4.6.0 (najnowsza wersja) nie ma -exitpredykatu .
Dennis
0

To wszystko wspaniałe rzeczy - właśnie zrobiłem to ze skryptu, aby sprawdzić puste katalogi poniżej bieżącego. Poniższe należy umieścić w pliku o nazwie „findempty”, umieścić gdzieś w ścieżce, aby bash mógł go znaleźć, a następnie uruchomić chmod 755. Sądzę, że można je łatwo dostosować do konkretnych potrzeb.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi
Warren Sherliker
źródło
0

Ta praca dla mnie ../INpolega na sprawdzaniu i przetwarzaniu plików w katalogu , biorąc pod uwagę, że skrypt znajduje się w ../Scriptkatalogu:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code
Arijit
źródło
0

Co z testowaniem, czy katalog istnieje i nie jest pusty w instrukcji if

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi
stanviv
źródło
-1

W przypadku dowolnego katalogu innego niż bieżący możesz sprawdzić, czy jest pusty, próbując rmdirgo otworzyć, ponieważ rmdirw przypadku niepustych katalogów z pewnością wystąpi błąd. Jeśli się rmdirpowiedzie i rzeczywiście chcesz, aby pusty katalog przetrwał test, po prostu mkdirto znowu.

Nie używaj tego hacka, jeśli istnieją inne procesy, które mogą zostać rozczłonkowane przez katalog, o którym wiedzą, że na krótko przestają istnieć.

Jeśli rmdirto nie zadziała, a być może testujesz katalogi, które potencjalnie mogą zawierać dużą liczbę plików, każde rozwiązanie oparte na globowaniu powłoki może się spowolnić i / lub przekroczyć limity długości wiersza poleceń. Prawdopodobnie lepiej użyć findw takim przypadku. findTak wygląda najszybsze rozwiązanie, jakie mogę wymyślić

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

Działa to w wersjach GNU i BSD, findale nie w wersji Solaris, której brakuje każdego z tych findoperatorów. Kochaj swoją pracę, Oracle.

flabdablet
źródło
To nie jest dobry pomysł. OP chciał po prostu sprawdzić, czy katalog jest pusty, czy nie.
roaima
-3

Możesz spróbować usunąć katalog i poczekać na błąd; rmdir nie usunie katalogu, jeśli nie jest pusty.

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi
impxd
źródło
3
-1 To jest rodzaj kodu, który odpala. rmdirzakończy się niepowodzeniem, jeśli nie będę mieć uprawnień do usunięcia katalogu; lub jeśli jest to podobjętość Btrfs; lub jeśli należy do systemu plików tylko do odczytu. A jeśli rmdirnie zawiedzie i mkdiruruchomi się: co jeśli już usunięty katalog należał do innego użytkownika? co z jego (być może niestandardowymi) uprawnieniami? ACL? rozszerzone atrybuty? Wszystko stracone.
Kamil Maciorowski
Cóż, właśnie uczę się bash i pomyślałem, że może to być szybszy sposób niż iteracja przez cały katalog, ale procesory są mocne i masz rację, nie jesteś bezpieczny.
impxd