Moja tldr odpowiedź brzmi:
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/..?*" ]
}
Jest zgodny z POSIX i, choć nie ma to większego znaczenia, zwykle jest szybszy niż rozwiązanie, które wyświetla katalog i przesyła dane wyjściowe do grep.
Stosowanie:
if emptydir adir
then
echo "nothing found"
else
echo "not empty"
fi
Podoba mi się odpowiedź https://unix.stackexchange.com/a/202276/160204 , którą przepisuję jako:
function emptydir {
! { ls -1qA "./$1/" | grep -q . ; }
}
Wyświetla katalog i potokuje wynik do grep. Zamiast tego proponuję prostą funkcję opartą na globalnej ekspansji i porównaniu.
function emptydir {
[ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}
Ta funkcja nie jest standardowym POSIX-em i wywołuje podpowłokę z $()
. Najpierw wyjaśniam tę prostą funkcję, aby później lepiej zrozumieć ostateczne rozwiązanie (patrz odpowiedź tldr powyżej).
Wyjaśnienie:
Lewa strona (LHS) jest pusta, gdy nie ma rozwinięcia, co ma miejsce, gdy katalog jest pusty. Opcja nullglob jest wymagana, ponieważ w przeciwnym razie, gdy nie ma dopasowania, glob jest wynikiem rozszerzenia. (Gdy RHS pasuje do globów LHS, gdy katalog jest pusty, nie działa z powodu fałszywych trafień, które występują, gdy glob LHS pasuje do pojedynczego pliku o nazwie jako glob: *
glob w dopasowuje podłańcuch *
w nazwie pliku. ) Wyrażenie nawiasów {,.[^.],..?}
obejmuje ukryte pliki, ale nie ..
lub .
.
Ponieważ shopt -s nullglob
jest wykonywany wewnątrz $()
(podpowłoki), nie zmienia nullglob
opcji bieżącej powłoki, co zwykle jest dobrą rzeczą. Z drugiej strony dobrym pomysłem jest ustawienie tej opcji w skryptach, ponieważ podatność na błędy powoduje, że glob zwraca coś, gdy nie ma dopasowania. Można więc ustawić opcję nullglob na początku skryptu i nie będzie ona potrzebna w funkcji. Pamiętajmy o tym: chcemy rozwiązania, które działa z opcją nullglob.
Ostrzeżenia:
Jeśli nie mamy dostępu do odczytu katalogu, funkcja zgłasza to samo, jakby był pusty katalog. Dotyczy to również funkcji, która wyświetla katalog i grep wyjście.
shopt -s nullglob
Komenda nie jest standardem POSIX.
Używa podpowłoki utworzonej przez $()
. To nie jest wielka sprawa, ale fajnie, jeśli możemy tego uniknąć.
Zawodowiec:
Nie, żeby to naprawdę miało znaczenie, ale ta funkcja jest czterokrotnie szybsza niż poprzednia, mierzona ilością czasu procesora spędzonego w jądrze w procesie.
Inne rozwiązania:
Możemy usunąć non POSIX shopt -s nullglob
polecenie na LHS i umieścić napis "$1/* $1/.[^.]* $1/..?*"
w RHS i wyeliminować oddzielnie fałszywych alarmów, które występują, gdy mamy tylko pliki o nazwie '*'
, .[^.]*
lub ..?*
w katalogu:
function emptydir {
[ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
[ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}
Bez shopt -s nullglob
polecenia sensowne jest teraz usunięcie podpowłoki, ale musimy zachować ostrożność, ponieważ chcemy uniknąć podziału słów, a jednocześnie umożliwić globalną ekspansję w LHS. W szczególności cytowanie w celu uniknięcia dzielenia słów nie działa, ponieważ zapobiega także globalnej ekspansji. Nasze rozwiązanie polega na osobnym rozpatrzeniu globów:
function emptydir {
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Wciąż mamy dzielenie słów dla poszczególnych globów, ale teraz jest w porządku, ponieważ spowoduje to błąd tylko wtedy, gdy katalog nie będzie pusty. Dodaliśmy 2> / dev / null, aby odrzucić komunikat o błędzie, gdy na LHS jest wiele plików pasujących do podanego globu.
Przypominamy, że chcemy rozwiązania, które będzie działać również z opcją nullglob. Powyższe rozwiązanie kończy się niepowodzeniem w przypadku opcji nullglob, ponieważ gdy katalog jest pusty, LHS również jest pusty. Na szczęście nigdy nie mówi się, że katalog jest pusty, gdy nie jest. Nie mówi tylko, że jest pusty, kiedy jest. Możemy więc zarządzać opcją nullglob osobno. Nie możemy po prostu dodać przypadków [ "$1/"* = "" ]
itp., Ponieważ będą się one rozwijać jako [ = "" ]
itd., Które są niepoprawne pod względem składniowym. [ "$1/"* "" = "" ]
Zamiast tego używamy itp. Znów trzeba rozważyć trzy przypadki *
, ..?*
i .[^.]*
dopasować ukryte pliki, ale nie .
i..
. Nie będą przeszkadzać, jeśli nie będziemy mieli opcji nullglob, ponieważ nigdy też nie mówią, że jest pusta, gdy nie jest. Tak więc końcowe proponowane rozwiązanie to:
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/..?*" ]
}
Zagadnienie bezpieczeństwa:
Utwórz dwa pliki rm
i x
pustego katalogu i uruchomić *
na monit. Glob *
rozszerzy się rm x
i zostanie on wykonany w celu usunięcia x
. Nie dotyczy to bezpieczeństwa, ponieważ w naszej funkcji globusy znajdują się tam, gdzie rozszerzenia nie są postrzegane jako polecenia, ale jako argumenty, tak jak w for f in *
.