2013-03-09 7 views
8

Ich versuche, ein einfaches Skript zu schreiben, das mir sagen wird, ob ein Dateiname in $ Temp existiert, der mit der Zeichenfolge "Test" beginnt.Shell-Skript, um zu überprüfen, ob die Datei existiert

Zum Beispiel, ich habe diese Dateien

Test1989.txt 
Test1990.txt 
Test1991.txt 

Dann echo Ich möchte nur, dass eine Datei gefunden wurde.

Zum Beispiel so etwas wie diese:

file="home/edward/bank1/fiche/Test*" 
if test -s "$file" 
then 
    echo "found one" 
else 
    echo "found none" 
fi 

Aber das funktioniert nicht.

+0

Die Frage "Shell" gibt - welche? Die Antwort, die ich hier für am besten halte, ist Bash-spezifisch; ist das angemessen oder zielst du auf etwas anderes? –

Antwort

-1

Platzhalterzeichen werden nicht innerhalb von Strings in Anführungszeichen erweitert. Und wenn Wildcard erweitert wird, wird es unverändert zurückgegeben, wenn keine Übereinstimmungen vorhanden sind. Es wird nicht in eine leere Zeichenfolge erweitert. Versuchen:

output="$(ls home/edward/bank1/fiche/Test* 2>/dev/null)" 
if [ -n "$output" ] 
then echo "Found one" 
else echo "Found none" 
fi 

Wenn der Platzhalter Dateinamen erweitert, ls sie auf stdout auflistet; Andernfalls wird ein Fehler auf stderr und nichts auf stdout gedruckt. Der Inhalt von stdout ist output zugeordnet.

if [ -n "$output" ] testet ob $output alles enthält.

Ein anderer Weg, dies würde zu schreiben sein:

if [ $(ls home/edward/bank1/fiche/Test* 2>/dev/null | wc -l) -gt 0 ] 
+0

Wofür steht -n? – Edward

+0

'test -n ' ist wahr, wenn '' nicht leer ist. – Barmar

+0

Die Verwendung von 'ls' wird hier nicht korrekt funktionieren, wenn mehr als eine passende Datei vorhanden ist. Besser, einen Glob-Ausdruck zu verwenden. Siehe auch http://mywiki.wooledge.org/BashFAQ/004 –

8

Ein Ansatz:

(
    shopt -s nullglob 
    files=(/home/edward/bank1/fiche/Test*) 
    if [[ "${#files[@]}" -gt 0 ]] ; then 
    echo found one 
    else 
    echo found none 
    fi 
) 

Erläuterung:

  • shopt -s nullglob wird /home/edward/bank1/fiche/Test* dazu führen nichts zu erweitern wenn keine Datei passt zu diesem Muster. (Ohne es bleibt es intakt.)
  • (...) richtet eine Subshell ein, die verhindert, dass shopt -s nullglob "entweicht".
  • files=(/home/edward/bank1/fiche/Test*) verschiebt die Dateiliste in ein Array mit dem Namen files. (Beachten Sie, dass dies nur innerhalb der Untershell ist; files ist nach dem Beenden der Subshell nicht verfügbar.)
  • "${#files[@]}" ist die Anzahl der Elemente in diesem Array.

Edited nachfolgende Frage („Was ist, wenn ich muss auch prüfen, ob diese Dateien Daten in ihnen und sind nicht Null-Byte-Dateien“) Adresse:

Für diese Version wir müssen -s (wie Sie in Ihrer Frage getan haben), die auch für die Existenz der Datei prüft, so gibt es keinen Sinn shopt -s nullglob mehr verwenden: Wenn keine Datei das Muster entspricht, dann -s auf das Muster wird falsch sein. Wir können also schreiben:

(
    found_nonempty='' 
    for file in /home/edward/bank1/fiche/Test* ; do 
    if [[ -s "$file" ]] ; then 
     found_nonempty=1 
    fi 
    done 
    if [[ "$found_nonempty" ]] ; then 
    echo found one 
    else 
    echo found none 
    fi 
) 

(hier die (...) ist file und found_file von „Flucht“ zu verhindern.)

+0

Was, wenn ich auch überprüfen muss, dass diese Dateien Daten in ihnen haben und keine 0-Byte-Dateien sind – Edward

+0

Dann müssen Sie eine Schleife wie 'für die Datei in $ {files [@]} schreiben" ' – Barmar

+0

Dies ist eine gute Antwort , und ich habe + 1'd es - meine einzige Behauptung ist, dass die Verwendung einer Subshell unnötig ist, und es macht nicht unbedingt Sinn, eine unnötige 'fork()' zu ermutigen. Wenn das Ziel darin besteht, den globalen Namespace zu vermeiden, würde das Erstellen einer Funktion und das Verwenden von Locals ausreichen. –

3

Sie können es in einer Zeile tun:

ls /home/edward/bank1/fiche/Test* >/dev/null 2>&1 && echo "found one" || echo "found none" 

Um zu verstehen, was es tut Sie den Befehl zu zersetzen haben und haben ein grundlegendes Bewusstsein für Boolesche Logik.

Direkt von bash man-Seite:

[...] 
expression1 && expression2 
    True if both expression1 and expression2 are true. 
expression1 || expression2 
    True if either expression1 or expression2 is true. 
[...] 

In der Schale (und in der Regel in Unix-Welt), die boolean true ist ein Programm, mit dem Status beendet 0

ls versuchen die zur Liste Muster, wenn es erfolgreich ist (dh das Muster existiert), wird es mit dem Status 0, 2 beendet (andernfalls siehe ls man page für Details).

In unserem Fall gibt es tatsächlich drei Ausdrücke, aus Gründen der Klarheit werde ich Klammern setzen, obwohl sie nicht, weil && benötigt werden, hat Vorrang auf ||:

(expression1 && expression2) || expression3 

so, wenn expression1 wahr ist (dh: ls das Muster gefunden) wertet expression2 aus (was nur ein Echo ist und mit dem Status 0 endet). In diesem Fall wird expression3 nie ausgewertet, weil das, was auf der linken Seite von || steht, bereits wahr ist und es eine Verschwendung von Ressourcen wäre, zu versuchen, zu bewerten, was rechts ist.

Andernfalls, wenn Ausdruck1 falsch ist, wird Ausdruck2 nicht ausgewertet, aber in diesem Fall Ausdruck3 ist.

+2

Das ist unnötig teuer. 'ls' ist ein externes Programm und nicht eine eingebaute Shell, also ruft es Forks auf. Die Shell kann nach Dateien suchen, ohne _y_ externe Programme aufzurufen - das tun schließlich die Glob-Ausdrücke. Außerdem verbringt "ls" Zeit und CPU sortiert seine Argumente und druckt sie (auch wenn nur zu/dev/null); usw. –

3

Sie müssen verstehen, wie Unix Ihre Eingabe interpretiert.

Die Standard-Unix-Shell interpoliert Umgebungsvariablen und so genannte Globs, bevor es die Parameter an Ihr Programm übergibt. Dies unterscheidet sich ein wenig von Windows, wodurch das Programm die Erweiterung interpretiert.

Versuchen Sie folgendes:

$ echo * 

Diese alle Dateien wird Echo und Verzeichnisse im aktuellen Verzeichnis. Bevor der Befehl echo ausgeführt wird, interpoliert die Shell den Wert *, erweitert ihn und übergibt diesen erweiterten Parameter dann an Ihren Befehl. Sie können ihn in Aktion sehen, indem Sie diese:

$ set -xv 
$ echo * 
$ set +xv 

Die set -xv schaltet xtrace und ausführliche. Verbose gibt den eingegebenen Befehl zurück und xtrace echos der Befehl, der ausgeführt wird (dh nach der Shell-Erweiterung).

Jetzt versuchen Sie dies:

$ echo "*" 

Beachten Sie, dass die glob Ausdruck aus der Schale etwas in Anführungszeichen setzen versteckt, und die Schale nicht erweitern kann.Versuchen Sie das:

Beachten Sie, dass die Shell Umgebungsvariablen in doppelte Anführungszeichen noch erweitern kann, aber nicht in einfache Anführungszeichen.

Kommen wir nun zu Ihrer Aussage aussehen:

file="home/edward/bank1/fiche/Test*" 

Die doppelte Anführungszeichen verhindern, dass die Schale aus Erweiterung des glob Ausdruck, so file gleich dem wörtlichen home/edward/bank1/finche/Test*. Daher müssen Sie, dies zu tun: (! Und der einleitende slash was wichtig ist)

file=/home/edward/bank1/fiche/Test* 

Das Fehlen von Zitaten wird nun Datei gleich alle Dateien, die diesen Ausdruck entsprechen. (Es könnte mehr als eins sein!). Wenn es keine Dateien gibt, kann die Shell, abhängig von der Shell und ihren Einstellungen, die Datei einfach auf diese literale Zeichenfolge setzen.

Sie haben sicherlich die richtige Idee:

file=/home/edward/bank1/fiche/Test* 
if test -s $file 
    then 
     echo "found one" 
    else 
     echo "found none" 
fi 

aber man könnte noch erhalten keine gefunden zurückgegeben, wenn es mehr als eine Datei ist. Stattdessen erhalten Sie möglicherweise einen Fehler in Ihrem test-Befehl, da zu viele Parameter vorhanden sind.

Eine Möglichkeit, dies zu umgehen könnte sein:

if ls /home/edward/bank1/finche/Test* > /dev/null 2>&1 
then 
    echo "There is at least one match (maybe more)!" 
else 
    echo "No files found" 
fi 

In diesem Fall nehme ich die Vorteile der Exit-Code des ls Befehl. Wenn ls eine Datei findet, auf die es zugreifen kann, gibt es einen Null-Beendigungscode zurück. Wenn keine übereinstimmende Datei gefunden werden kann, wird ein Beendigungscode ungleich null zurückgegeben. Der Befehl if führt lediglich einen Befehl aus, und wenn der Befehl eine Null zurückgibt, nimmt er die if-Anweisung als wahr an und führt die if-Klausel aus. Wenn der Befehl einen Wert ungleich Null zurückgibt, wird angenommen, dass die if-Anweisung falsch ist, und die else-Klausel (falls eine verfügbar ist) wird ausgeführt.

Der Befehl test funktioniert in ähnlicher Weise. Wenn test wahr ist, gibt der test Befehl eine Null zurück. Andernfalls gibt der Befehl test einen Wert ungleich null zurück. Dies funktioniert hervorragend mit dem Befehl if. In der Tat gibt es einen Alias ​​ zum test Befehl. Versuchen Sie folgendes:

$ ls -li /bin/test /bin/[ 

Die i druckt die inode. Die Inode ist die echte ID der Datei. Dateien mit derselben ID sind dieselbe Datei. Sie können sehen, dass /bin/test und /bin/[ derselbe Befehl sind.Dies macht die folgenden zwei Befehle die gleiche:

if test -s $file 
then 
    echo "The file exists" 
fi 

if [ -s $file ] 
then 
    echo "The file exists" 
fi 
0

Das folgende Skript wird dazu beitragen, u ein Verfahren zu gehen, wenn das Skript in einer bestimmten Variablen existieren,

cat > waitfor.csh 

    #!/bin/csh 

    while !(-e $1) 

    sleep 10m 

    end 

ctrl+D 

hier -e ist für die Arbeit mit Dateien ,

$1 ist ein Shell-Variable,

Schlaf für 10 Minuten

u kann das Skript von ./waitfor.csh ./temp ; echo "the file exits"

1
for entry in "/home/loc/etc/"/* 
do 

    if [ -s /home/loc/etc/$entry ] 
    then 
     echo "$entry File is available" 
    else 
     echo "$entry File is not available" 
fi 
done 

Hope it helps 
0

One-Liner ausführen Datei zu überprüfen vorhanden sind oder nicht -

awk 'BEGIN {print getline < "file.txt" < 0 ? "File does not exist" : "File Exists"}' 
+0

OP benötigt, um nach einer Datei mit einem gegebenen Namensmuster in einem bestimmten Verzeichnis zu suchen, und er brauchte es, um nicht leer zu sein. – blackpen

Verwandte Themen