Wie überprüfe ich, ob eine Datei in Go vorhanden ist?

435

Die Standardbibliothek von Go verfügt nicht über eine Funktion, mit der ausschließlich überprüft werden soll, ob eine Datei vorhanden ist oder nicht (wie bei Python os.path.exists). Was ist der idiomatische Weg, um es zu tun?

Sridhar Ratnakumar
quelle
Ich verstehe es nicht wirklich. Gleichzeitig sagen Sie, dass es keine Standardfunktion gibt, und Sie schreiben eine Antwort mit der Standardfunktion. Was vermisse ich ? Sollte nicht zumindest die Frage behoben werden?
Denys Séguret
@dystroy - Die Frage wurde behoben.
Sridhar Ratnakumar
11
Man sollte es besser vermeiden, nach der Existenz einer Datei zu fragen. Aufgrund der rassigen Natur der Antwort sagen die erhaltenen Informationen, dass in der angeforderten Zeit tatsächlich nichts Nützliches über der Datei vorhanden war - aber möglicherweise nicht mehr. Der empfohlene Weg besteht darin, einfach eine Datei zu öffnen und zu überprüfen, ob dies fehlschlägt oder nicht.
zzzz
2
Dies wurde bereits beantwortet hier
Sergey Koulikov
2
@zzzz (Ich weiß, es ist Jahre her, dieser Kommentar ist für neue Leser) Ich stimme im allgemeinen Fall zu. Meine App lädt jedoch eine Bibliothek eines Drittanbieters, die einen Dateipfad als Initialisierungsdaten verwendet, jedoch Fehler verursacht, wenn die Datei nicht vorhanden ist. Ich denke, dies ist ein gültiges Szenario, um zu überprüfen, ob die Datei vorhanden ist, ohne zu versuchen, sie zu öffnen, um den Fehler ohne einen schwerwiegenden Absturz melden zu können, da mein Code den Dateiinhalt nicht lesen oder direkt in die Datei schreiben muss.
Sergio Acosta

Antworten:

690

Um zu überprüfen, ob eine Datei nicht vorhanden ist, entspricht Python if not os.path.exists(filename):

if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
  // path/to/whatever does not exist
}

So überprüfen Sie, ob eine Datei vorhanden ist, die Python entspricht if os.path.exists(filename):

Bearbeitet: gemäß den letzten Kommentaren

if _, err := os.Stat("/path/to/whatever"); err == nil {
  // path/to/whatever exists

} else if os.IsNotExist(err) {
  // path/to/whatever does *not* exist

} else {
  // Schrodinger: file may or may not exist. See err for details.

  // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence


}
Sridhar Ratnakumar
quelle
3
manchmal gibt es ENOTDIR zurück, anstatt NOTEXISTzum Beispiel, falls /etc/bashrcvorhanden, das /etc/bashrc/foobarwird zurückkehrenENOTDIR
lidaobing
43
Das zweite Snippet ist subtiler falsch; die Bedingung sollte sein !os.IsNotExist(err). Es ist möglich, dass die Datei vorhanden ist, aber os.Stataus anderen Gründen fehlschlägt (z. B. Berechtigung, fehlerhafte Festplatte). Wenn Sie err == nilals Bedingung verwenden, werden Fehler fälschlicherweise als "Die Datei existiert nicht" kategorisiert.
Woche
9
Zu überprüfen, ob eine Datei existiert, ist falsch: err ist null, wenn eine Datei existiert
tangxinfa
1
Stellen
Marcello de Sales
Sie könnten je nach Fall os.IsExist () verwenden, könnten idiomatischer sein, anstatt eine doppelte Negation zu machen, wenn Sie! Os.IsNotExistant ()
Ariel Monaco
126

Antwort von Caleb Spare in der Gonuts- Mailingliste.

[...] Es wird eigentlich nicht sehr oft benötigt und [...] die Verwendung os.Statist für die Fälle, in denen es erforderlich ist, einfach genug.

[...] Zum Beispiel: Wenn Sie die Datei öffnen möchten, gibt es keinen Grund, zuerst zu prüfen, ob sie vorhanden ist. Die Datei kann zwischen dem Überprüfen und Öffnen verschwinden, und Sie müssen den os.OpenFehler trotzdem überprüfen . Sie rufen einfach an, os.IsNotExist(err)nachdem Sie versucht haben, die Datei zu öffnen, und kümmern sich dort um ihre Nichtexistenz (wenn dies eine besondere Behandlung erfordert).

[...] Sie müssen nicht nach vorhandenen Pfaden suchen (und sollten dies auch nicht tun).

  • os.MkdirAllfunktioniert unabhängig davon, ob die Pfade bereits vorhanden sind oder nicht. (Außerdem müssen Sie den Fehler dieses Aufrufs überprüfen.)

  • Anstatt zu verwenden os.Create, sollten Sie verwenden os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666). Auf diese Weise erhalten Sie eine Fehlermeldung, wenn die Datei bereits vorhanden ist. Dies hat auch keine Race-Bedingung, bei der die Datei von etwas anderem erstellt wird, im Gegensatz zu Ihrer Version, die zuvor die Existenz überprüft.

Entnommen aus: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J

OscarRyz
quelle
30

Sie sollten die Funktionen os.Stat()und os.IsNotExist()wie im folgenden Beispiel verwenden:

// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
    if _, err := os.Stat(name); err != nil {
        if os.IsNotExist(err) {
            return false
        }
    }
    return true
}

Das Beispiel wird aus extrahiert hier .

Afriza N. Arief
quelle
12
Achtung: Wie stackoverflow.com/a/22467409/712014 hervorhob , gibt dieser Code true zurück, auch wenn die Datei nicht vorhanden ist, z. B. wenn Stat () die verweigerte Berechtigung zurückgibt.
Michael
19

Das Beispiel von user11617 ist falsch; Es wird gemeldet, dass die Datei auch dann vorhanden ist, wenn dies nicht der Fall ist, aber ein anderer Fehler aufgetreten ist.

Die Signatur sollte Exists (Zeichenfolge) sein (Bool, Fehler). Und dann sind die Anrufseiten nicht besser.

Der Code, den er schrieb, wäre besser als:

func Exists(name string) bool {
    _, err := os.Stat(name)
    return !os.IsNotExist(err)
}

Aber ich schlage stattdessen Folgendes vor:

func Exists(name string) (bool, error) {
  _, err := os.Stat(name)
  if os.IsNotExist(err) {
    return false, nil
  }
  return err != nil, err
}
user3431012
quelle
7
Was ist Beispiel 5? Könnten Sie bitte genau sein.
xlm
1
Ihr zweites Beispiel muss mehrere Rückgabewerte zerstören - zB _, err: = os.Stat (Name)
David Duncan
6
Warum zurückkehren err != nilstatt err == nil? Wenn es einen Fehler gibt, existiert die Datei wahrscheinlich nicht?
idbrii
14

Was andere Antworten übersehen haben, ist, dass der Pfad, der der Funktion gegeben wird, tatsächlich ein Verzeichnis sein kann. Die folgende Funktion stellt sicher, dass der Pfad wirklich eine Datei ist.

func fileExists(filename string) bool {
    info, err := os.Stat(filename)
    if os.IsNotExist(err) {
        return false
    }
    return !info.IsDir()
}

Noch etwas zu beachten: Dieser Code kann immer noch zu einer Race-Bedingung führen, bei der ein anderer Thread oder Prozess die angegebene Datei löscht oder erstellt, während die Funktion fileExists ausgeführt wird.

Wenn Sie sich darüber Sorgen machen, verwenden Sie eine Sperre in Ihren Threads, serialisieren Sie den Zugriff auf diese Funktion oder verwenden Sie ein prozessübergreifendes Semaphor, wenn mehrere Anwendungen beteiligt sind. Wenn andere Anwendungen beteiligt sind, die außerhalb Ihrer Kontrolle liegen, haben Sie vermutlich kein Glück.

ZuBsPaCe
quelle
12
    _, err := os.Stat(file)
    if err == nil {
        log.Printf("file %s exists", file)
    } else if os.IsNotExist(err) {
        log.Printf("file %s not exists", file)
    } else {
        log.Printf("file %s stat error: %v", file, err)
    }
Tangxinfa
quelle
7

Das Funktionsbeispiel:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}
honmaple
quelle
1
Ist das nicht überflüssig?
Ilia Choly
6

Schauen wir uns zunächst einige Aspekte an. Beide Funktionen des osPakets golangsind keine Dienstprogramme, sondern Fehlerprüfer. Damit meine ich, dass sie nur ein Wrapper sind, um Fehler auf plattformübergreifenden Fehlern zu behandeln.

Also im Grunde , wenn , os.Statwenn diese Funktion nicht einen Fehler nicht geben , dass die Datei bedeutet , vorhanden ist, wenn es tut müssen Sie überprüfen , was es Art von Fehler ist, hier die Verwendung dieser beiden Funktion kommt os.IsNotExistund os.IsExist.

Dies kann als StatFehler beim Auslösen von Dateien verstanden werden, weil er nicht vorhanden ist, oder als Fehler beim Auslösen, weil er vorhanden ist und ein Problem damit vorliegt.

Der Parameter, den diese Funktionen annehmen, ist vom Typ error, obwohl Sie ihn möglicherweise übergeben können, nilaber er macht keinen Sinn.

Dies weist auch auf die Tatsache hin, dass IsExist is not same as !IsNotExistes sich um zwei verschiedene Dinge handelt.

Wenn Sie nun wissen möchten, ob eine bestimmte Datei in go vorhanden ist, würde ich den besten Weg bevorzugen:

if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
   //TODO
} 
Farhaan Bukhsh
quelle
1

Wie in anderen Antworten erwähnt, ist es möglich, das erforderliche Verhalten / die erforderlichen Fehler aus der Verwendung verschiedener Flags mit zu konstruieren os.OpenFile. In der Tat os.Createist nur eine sinnvolle Standardeinstellung dafür:

// Create creates or truncates the named file. If the file already exists,
// it is truncated. If the file does not exist, it is created with mode 0666
// (before umask). If successful, methods on the returned File can
// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
    return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

Sie sollten diese Flags selbst kombinieren, um das Verhalten zu erhalten, an dem Sie interessiert sind:

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
    // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
    O_RDONLY int = syscall.O_RDONLY // open the file read-only.
    O_WRONLY int = syscall.O_WRONLY // open the file write-only.
    O_RDWR   int = syscall.O_RDWR   // open the file read-write.
    // The remaining values may be or'ed in to control behavior.
    O_APPEND int = syscall.O_APPEND // append data to the file when writing.
    O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
    O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
    O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
    O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

Je nachdem, was Sie auswählen, werden unterschiedliche Fehler angezeigt.

Hier ist ein Beispiel, in dem ich eine Datei zum Schreiben öffnen möchte, eine vorhandene Datei jedoch nur abschneide, wenn der Benutzer angegeben hat, dass dies in Ordnung ist:

var f *os.File
if truncateWhenExists {
    // O_TRUNC - truncate regular writable file when opened.
    if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
        log.Fatalln("failed to force-open file, err:", err)
    }
} else {
    // O_EXCL - used with O_CREATE, file must not exist
    if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
        log.Fatalln("failed to open file, err:", err) 
   }
}
Sebastian N.
quelle
0

Der beste Weg, um zu überprüfen, ob eine Datei vorhanden ist:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}
AlSan
quelle