Mam wymaganie, jeśli wykonam skrypt ./123
z argumentami pustej ścieżki, powiedzmy /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions
(ten katalog jest pusty). Powinien zostać wyświetlony komunikat „katalog jest pusty”
Mój kod to:
#!/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
Mam komunikat o błędzie, np. Jeśli wykonam nazwę skryptu ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
(ten katalog jest pusty).
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
zamiast pokazywać dane wyjściowe „katalog jest pusty” pokazuje powyższe dane wyjściowe
Poniższe dane wyjściowe muszą zostać wyświetlone, jeśli skasuję skrypt za pomocą poprawnych argumentów (mam na myśli prawidłową ścieżkę katalogu). mówić./123 /usr/share
minimum file size: 196
/usr/share
maximum file size: 14096
/usr/share
average file size: 4000
moja oczekiwana wydajność to: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
directory is empty.
shell-script
buddha sreekanth
źródło
źródło
Odpowiedzi:
Powyższe jest testem zgodnym z POSIX - i powinno być bardzo szybkie.
ls
wyświetli listę wszystkich plików / katalogów z wyjątkiem jednego.
i..
każdego w wierszu oraz-q
uoteuje wszystkie znaki niedrukowalne (w tym\n
ewline) na wyjściu ze?
znakiem zapytania. W ten sposób, jeśligrep
odbierze choćby jeden znak wejściowy, zwróci true - w przeciwnym razie false.Aby to zrobić tylko w powłoce POSIX:
POSIX powłoki (która nie wcześniej wyłączone
-f
generacji ilename) będzie Tablica położenia parametr się albo do tekstowymi następnie przez polecenia powyżej, albo do pól wytwarzanych przez operatorów glob na końcu każdego z nich. To, czy tak się stanie, zależy od tego, czy globusy faktycznie coś pasują. W niektórych powłokach możesz poinstruować nierozdzielającą glob, aby rozwinął się do zera - albo wcale. Czasami może to być korzystne, ale nie jest przenośne i często wiąże się z dodatkowymi problemami - takimi jak konieczność ustawienia specjalnych opcji powłoki, a następnie ich rozbrojenia.set
"$@"
set
Jedynym przenośnym sposobem obsługi argumentów o wartości zerowej są puste lub nieuzbrojone zmienne lub
~
rozszerzenia tyldy. A ta druga, nawiasem mówiąc, jest znacznie bezpieczniejsza niż pierwsza.Nad powłoką testuje tylko dowolny plik pod kątem
-e
istnienia, jeśli żadna z trzech określonych globów nie rozwiązuje więcej niż jednego dopasowania.for
Pętla jest więc w ogóle uruchamiana tylko dla trzech lub mniej iteracji i tylko w przypadku pustego katalogu lub w przypadku, gdy jeden lub więcej wzorców jest rozwiązywanych tylko do jednego pliku.for
Równieżbreak
S Jeżeli którykolwiek z globs stanowią rzeczywisty plik - i jak ja urządzam globs w kolejności od najbardziej do najmniej prawdopodobne prawdopodobne, powinna ona prawie rzucić na pierwszej iteracji za każdym razem.Tak czy inaczej, powinno to dotyczyć tylko jednego
stat()
wywołania systemowego - powłoki ils
oba powinny tylko przeglądaćstat()
katalog i wyświetlać listę plików, które raportuje dentries . Kontrastuje to z zachowaniem,find
które zamiast tegostat()
zawierałby każdy plik, który można z nim wymienić.źródło
ls
sprawie. globusy i[
milczą, gdy nie mają dostępu. Na przykład[ -e /var/spool/cron/crontabs/stephane ]
po cichu zgłasza wartość fałsz, podczas gdy istnieje plik o tej nazwie.Dzięki GNU lub nowoczesnym BSD
find
możesz:(zakłada
$dir
nie wygląda jakfind
orzecznika (!
,(
,-name...
).POSIXly:
źródło
[-z $dir ]
narzeka, że w[-z
większości systemów nie ma polecenia . Potrzebujesz odstępów wokół nawiasów .[ -z $dir ]
zdarza się prawda, jeślidir
jest pusta i jest fałszywa dla większości innych wartościdir
, ale jest niewiarygodna, na przykład jest prawdą, jeśli wartośćdir
to= -z
lub-o -o -n -n
. Zawsze używaj podwójnych cudzysłowów wokół podstawień poleceń (dotyczy to również reszty skryptu).[ -z "$dir" ]
sprawdza, czy wartość zmiennejdir
jest pusta. Wartością zmiennej jest ciąg znaków, którym jest ścieżka do katalogu. To nic nie mówi o samym katalogu.Nie ma operatora sprawdzającego, czy katalog jest pusty, tak jak w przypadku zwykłego pliku (
[ -s "$dir" ]
dotyczy to katalogu, nawet jeśli jest pusty). Prostym sposobem sprawdzenia, czy katalog jest pusty, jest wyświetlenie jego zawartości; jeśli otrzymasz pusty tekst, katalog jest pusty.W starszych systemach, które nie mają
ls -A
, możesz użyćls -a
, ale wtedy.
i..
są na liście.(Nie wcinaj wiersza, który zaczyna się
..
od, ponieważ ciąg musi zawierać tylko nowy wiersz, a nie nowy wiersz i dodatkowe spacje.)źródło
$(…)
jest polecenie podstawieniePatrzysz na atrybuty samego katalogu.
Chcesz policzyć, ile jest tam plików:
Tak więc testem „katalog jest pusty” jest:
Lubię to:
źródło
Moja tldr odpowiedź brzmi:
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:
Podoba mi się odpowiedź https://unix.stackexchange.com/a/202276/160204 , którą przepisuję jako:
Wyświetla katalog i potokuje wynik do grep. Zamiast tego proponuję prostą funkcję opartą na globalnej ekspansji i porównaniu.
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 zmienianullglob
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: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: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:Zagadnienie bezpieczeństwa:
Utwórz dwa pliki
rm
ix
pustego katalogu i uruchomić*
na monit. Glob*
rozszerzy sięrm x
i zostanie on wykonany w celu usunięciax
. 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 wfor f in *
.źródło
$(set -f; echo "$1"/*)
wydaje się dość skomplikowanym sposobem pisania"$1/*"
. I to nie pasuje do ukrytych katalogów lub plików."$1/*"
(nota cytaty należą*
), więcset -f
i powłoki w tle są zbędne na które w szczególności../* ./.[!.]* ./..?*
). Być może możesz to włączyć, aby funkcja była kompletna.Oto kolejny prosty sposób na zrobienie tego. Załóżmy, że D jest pełną nazwą ścieżki katalogu, który chcesz przetestować pod kątem pustki.
Następnie
Działa to, ponieważ
ma jako wynik
awk usuwa D, a jeśli rozmiar wynosi 0, katalog jest pusty.
źródło
źródło
echo
, wszystko czego potrzebujesz tolist="$somedir/*"
. Jest to również trudne i podatne na błędy. Nie wspominałeś, że OP powinien podać ścieżkę docelowego katalogu, w tym na przykład ukośnik końcowy.