Ich habe eine Anforderung, wenn ich ein Skript ./123
mit Argumenten des leeren Pfads ausführe , sagen wir /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions
(dieses Verzeichnis ist leer). Es sollte "Verzeichnis ist leer" angezeigt werden.
Mein Code lautet:
#!/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
Ich habe eine Fehleranzeige wie, wenn ich den Skriptnamen ausführe ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
(dieses Verzeichnis ist leer).
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
Anstatt nur die Ausgabe "Verzeichnis ist leer" anzuzeigen, wird die obige Ausgabe angezeigt
Die folgende Ausgabe muss angezeigt werden, wenn ich das Skript mit korrekten Argumenten ausführe (ich meine mit korrektem Verzeichnispfad). sagen./123 /usr/share
minimum file size: 196
/usr/share
maximum file size: 14096
/usr/share
average file size: 4000
Meine erwartete Ausgabe ist: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
directory is empty.
quelle
Antworten:
Das Obige ist ein POSIX-kompatibler Test - und sollte sehr schnell sein.
ls
listet alle Dateien / Verzeichnisse in einem Verzeichnis mit Ausnahme von.
und..
jede pro Zeile auf und-q
markiert alle nicht druckbaren Zeichen (einschließlich\n
Ewlines) in der Ausgabe mit einem?
Fragezeichen. Auf diese Weise wird, wenngrep
nur ein einziges Zeichen in der Eingabe empfangen wird, true zurückgegeben - andernfalls false.So machen Sie es allein in einer POSIX-Shell:
Eine POSIX-Shell (die die
-f
Generierung von Dateinamen zuvor nicht deaktiviert hat) führtset
das"$@"
Positionsparameter-Array entweder zu den Literalzeichenfolgen, denen derset
obige Befehl folgt , oder zu den Feldern, die am Ende jeweils von Glob-Operatoren generiert werden. Ob dies der Fall ist, hängt davon ab, ob die Globs tatsächlich zu irgendetwas passen. In einigen Shells können Sie einen nicht auflösenden Glob anweisen, auf Null zu expandieren - oder gar nichts. Dies kann manchmal von Vorteil sein, ist jedoch nicht portabel und bringt häufig zusätzliche Probleme mit sich - beispielsweise das Festlegen spezieller Shell-Optionen und das anschließende Deaktivieren dieser Optionen.Das einzige tragbare Mittel zur Behandlung von Argumenten mit Nullwerten sind entweder leere oder nicht gesetzte Variablen oder
~
Tilde-Erweiterungen. Und das letztere ist übrigens weitaus sicherer als das erstere.Über der Shell wird nur eine der Dateien auf
-e
Xistenz getestet, wenn keiner der drei angegebenen Globs in mehr als eine einzelne Übereinstimmung aufgelöst wird. Diefor
Schleife wird also immer nur für drei oder weniger Iterationen ausgeführt und nur im Fall eines leeren Verzeichnisses oder im Fall, dass eines oder mehrere der Muster nur in eine einzelne Datei aufgelöst werden. Dasfor
auch,break
wenn einer der Globs eine tatsächliche Datei darstellt - und da ich die Globs in der Reihenfolge der wahrscheinlichsten bis unwahrscheinlichsten angeordnet habe, sollte sie jedes Mal bei der ersten Iteration so gut wie beendet werden.In beiden Fällen sollte es sich nur um einen einzigen Systemaufruf handeln
stat()
- die Shell - undls
beide müssen nurstat()
das abgefragte Verzeichnis benötigen und die Dateien auflisten , die die darin enthaltenen Einträge enthalten. Dem steht das Verhalten gegenüber,find
das stattdessenstat()
jede Datei enthält, die Sie möglicherweise auflisten.quelle
ls
Fall. Globs und[
schweigen, wenn sie keinen Zugriff haben. Zum Beispiel[ -e /var/spool/cron/crontabs/stephane ]
wird stillschweigend false gemeldet, während eine Datei mit diesem Namen vorhanden ist.Mit GNU oder modernen BSDs
find
können Sie Folgendes tun:(geht davon aus
$dir
sieht nicht wie einfind
Prädikat (!
,(
,-name...
).POSIXly:
quelle
[-z $dir ]
beschwert sich, dass[-z
auf den meisten Systemen kein Befehl aufgerufen wird. Sie benötigen Leerzeichen um die Klammern .[ -z $dir ]
geschieht um wahr zu sein , wenndir
leer ist, und für die meisten anderen Werte falsch istdir
, aber es ist unzuverlässig, zum Beispiel es ist wahr , wenn der Wertdir
ist= -z
oder-o -o -n -n
. Verwenden Sie bei Befehlsersetzungen immer doppelte Anführungszeichen (dies gilt auch für den Rest Ihres Skripts).[ -z "$dir" ]
testet, ob der Wert der Variablendir
leer ist. Der Wert der Variablen ist eine Zeichenfolge, die zufällig der Pfad zum Verzeichnis ist. Das sagt nichts über das Verzeichnis selbst aus.Es gibt keinen Operator zum Testen, ob ein Verzeichnis leer ist, wie es für eine reguläre Datei der
[ -s "$dir" ]
Fall ist ( gilt für ein Verzeichnis, auch wenn es leer ist). Eine einfache Möglichkeit zu testen, ob ein Verzeichnis leer ist, besteht darin, seinen Inhalt aufzulisten. Wenn Sie leeren Text erhalten, ist das Verzeichnis leer.Auf älteren Systemen, die nicht haben
ls -A
, können Sie verwendenls -a
, aber dann.
und..
werden aufgelistet.(Ziehen Sie die Zeile, mit
..
der begonnen wird, nicht ein, da die Zeichenfolge nur eine neue Zeile, keine neue Zeile und zusätzliches Leerzeichen enthalten muss.)quelle
$(…)
ist BefehlsersetzungSie sehen sich die Attribute des Verzeichnisses selbst an.
Sie möchten zählen, wie viele Dateien sich dort befinden:
Der Test für "Verzeichnis ist leer" lautet also:
So was:
quelle
Meine Antwort lautet:
Es ist POSIX-kompatibel und nicht viel wichtiger als die Lösung, die das Verzeichnis auflistet und die Ausgabe an grep weiterleitet.
Verwendung:
Ich mag die Antwort /unix//a/202276/160204 , die ich wie folgt umschreibe:
Es listet das Verzeichnis auf und leitet das Ergebnis an grep weiter. Stattdessen schlage ich eine einfache Funktion vor, die auf Glob-Erweiterung und Vergleich basiert.
Diese Funktion ist kein Standard-POSIX und ruft eine Subshell mit auf
$()
. Ich erkläre diese einfache Funktion zuerst, damit wir die endgültige Lösung (siehe die Antwort von tldr oben) später besser verstehen können.Erläuterung:
Die linke Seite (LHS) ist leer, wenn keine Erweiterung erfolgt. Dies ist der Fall, wenn das Verzeichnis leer ist. Die Option nullglob ist erforderlich, da andernfalls der Glob selbst das Ergebnis der Erweiterung ist, wenn keine Übereinstimmung vorliegt. (Wenn die RHS mit den Globs der LHS übereinstimmt, wenn das Verzeichnis leer ist, funktioniert dies nicht, da Fehlalarme auftreten, wenn ein LHS-Glob mit einer einzelnen Datei übereinstimmt, die als Glob selbst bezeichnet wird: Die
*
im Glob entspricht der Teilzeichenfolge*
im Dateinamen. ) Der Klammerausdruck{,.[^.],..?}
deckt versteckte Dateien ab, aber nicht..
oder.
.Da
shopt -s nullglob
es in$()
(einer Subshell) ausgeführt wird, ändert es nichts an dernullglob
Option der aktuellen Shell, was normalerweise eine gute Sache ist. Auf der anderen Seite ist es eine gute Idee, diese Option in Skripten festzulegen, da es fehleranfällig ist, wenn ein Glob etwas zurückgibt, wenn keine Übereinstimmung vorliegt. Man könnte also die Option nullglob am Anfang des Skripts setzen und sie wird in der Funktion nicht benötigt. Denken wir daran: Wir wollen eine Lösung, die mit der Option nullglob funktioniert.Vorsichtsmaßnahmen:
Wenn wir keinen Lesezugriff auf das Verzeichnis haben, meldet die Funktion dasselbe, als ob es ein leeres Verzeichnis gäbe. Dies gilt auch für eine Funktion, die das Verzeichnis auflistet und die Ausgabe erfasst.
Der
shopt -s nullglob
Befehl ist kein Standard-POSIX.Es verwendet die von erstellte Subshell
$()
. Es ist keine große Sache, aber es ist schön, wenn wir es vermeiden können.Profi:
Nicht, dass es wirklich wichtig wäre, aber diese Funktion ist viermal schneller als die vorherige, gemessen an der CPU-Zeit, die im Kernel innerhalb des Prozesses verbracht wird.
Andere Lösungen:
Wir können das nicht POSIX entfernen
shopt -s nullglob
Befehl auf der LHS und die Zeichenfolge setzen"$1/* $1/.[^.]* $1/..?*"
in der RHS und separat die Fehlalarme eliminieren , die auftreten , wenn wir nur Dateien mit dem Namen haben'*'
,.[^.]*
oder..?*
in dem Verzeichnis:Ohne den
shopt -s nullglob
Befehl ist es jetzt sinnvoll, die Unterschale zu entfernen, aber wir müssen vorsichtig sein, da wir die Wortteilung vermeiden und dennoch eine Glob-Erweiterung auf der LHS zulassen möchten. Insbesondere das Zitieren zur Vermeidung von Wortaufteilungen funktioniert nicht, da es auch die Glob-Erweiterung verhindert. Unsere Lösung besteht darin, die Globs separat zu betrachten:Wir haben immer noch eine Wortaufteilung für den einzelnen Glob, aber jetzt ist es in Ordnung, da dies nur dann zu einem Fehler führt, wenn das Verzeichnis nicht leer ist. Wir haben 2> / dev / null hinzugefügt, um die Fehlermeldung zu verwerfen, wenn viele Dateien mit dem angegebenen Glob auf der LHS übereinstimmen.
Wir erinnern uns, dass wir eine Lösung wollen, die auch mit der Nullglob-Option funktioniert. Die obige Lösung schlägt mit der Option nullglob fehl, da bei einem leeren Verzeichnis auch die LHS leer sind. Glücklicherweise heißt es nie, dass das Verzeichnis leer ist, wenn dies nicht der Fall ist. Es kann nur nicht gesagt werden, dass es leer ist, wenn es ist. Daher können wir die Nullglob-Option separat verwalten. Wir können die Fälle
[ "$1/"* = "" ]
usw. nicht einfach hinzufügen, da diese als[ = "" ]
usw. erweitert werden und syntaktisch falsch sind. Also verwenden wir[ "$1/"* "" = "" ]
stattdessen etc. Wir müssen wieder die drei Fälle betrachten*
,..?*
und.[^.]*
die versteckten Dateien passen, aber nicht.
und..
. Diese stören nicht, wenn wir die Option nullglob nicht haben, da sie auch nie sagen, dass sie leer ist, wenn sie nicht leer ist. Die endgültige vorgeschlagene Lösung lautet also:Sicherheitsbedenken:
Erstellen Sie zwei Dateien
rm
undx
in ein leeres Verzeichnis , und führen Sie*
auf der Eingabeaufforderung. Der Glob*
wird auf erweitertrm x
und ausgeführt, um ihn zu entfernenx
. Dies ist kein Sicherheitsbedenken, da sich in unserer Funktion die Globs dort befinden, wo die Erweiterungen nicht als Befehle, sondern als Argumente angesehen werden, genau wie infor f in *
.quelle
$(set -f; echo "$1"/*)
scheint eine ziemlich komplizierte Art zu schreiben"$1/*"
. Und dies stimmt nicht mit versteckten Verzeichnissen oder Dateien überein."$1/*"
( man beachte die Anführungszeichen enthalten*
), so dass dieset -f
und Subshell für unnötig ist , dass insbesondere../* ./.[!.]* ./..?*
). Vielleicht können Sie das einbauen, um die Funktion zu vervollständigen.quelle
echo
, alles was Sie brauchen istlist="$somedir/*"
. Dies ist auch schwierig und fehleranfällig. Sie haben nicht erwähnt, dass das OP den Pfad des Zielverzeichnisses angeben soll, einschließlich beispielsweise des abschließenden Schrägstrichs.Hier ist eine andere einfache Möglichkeit, dies zu tun. Angenommen, D ist der vollständige Pfadname des Verzeichnisses, das Sie auf Leere testen möchten.
Dann
Das funktioniert weil
hat als Ausgabe
awk entfernt das D und wenn die Größe 0 ist, ist das Verzeichnis leer.
quelle