Node: Ärgernis: fs.exists/existsSync verwerfen

Erstellt am 2. Mai 2015  ·  207Kommentare  ·  Quelle: nodejs/node

Nachdem ich in den Node-Dokumenten festgestellt hatte, dass fs.exists und fs.existsSync veraltet sein würden, habe ich mir die iojs-Dokumente angesehen, um zu sehen, dass dies tatsächlich der Fall war.

Das ist _wirklich ärgerlich_ und scheint auf der Annahme zu beruhen, dass jeder Entwickler immer nur die Existenz einer Datei prüft, bevor er in einem asynchronen Kontext liest/schreibt. Während ich die Stimmung hinter dem Wunsch verstehe, „unnötige Abstraktionen“ oder Race-Conditions vermeiden zu wollen, ist _this_ unnötig.

Während ich früher in der Lage war, die Existenz einer Datei mit einer einzigen booleschen Variablen zu überprüfen, habe ich jetzt keine andere Wahl, als meinen Code entweder zu versuchen/abzufangen oder asynchron zu machen und auf Fehlerereignisse zu lauschen.

Dies fühlt sich wie Präskriptivismus an, da mir kein einziger Grund einfällt, warum eine strenge Warnung vor den möglichen Auswirkungen und / oder Beispielen für Vorbehalte bei seiner Verwendung nicht ausgereicht hätte.

Kann mir jemand helfen zu verstehen, warum dies notwendig war (über das All-Async-All-the-Time-Paradigma hinaus (das nicht immer unbedingt zutrifft (insbesondere im Fall von synchronen CLI-Tools)))?

Oder kann ich vielleicht einfach eine PR einreichen, die diese vollkommen gute Funktionalität _nicht mehr unterstützt_?

BEARBEITEN: _Ich stelle gerne zusätzliche Unterlagen zur Verfügung, die als notwendig erachtet werden._

feature request fs

Hilfreichster Kommentar

oder warum nicht den Wrapper so lassen, wie er ist, und mich weiterhin fs.existsSync verwenden lassen? :smile_cat:

Alle 207 Kommentare

Das ist wirklich ärgerlich und scheint auf der Annahme zu beruhen, dass jeder Entwickler nur die Existenz einer Datei überprüft, bevor er in einem asynchronen Kontext liest/schreibt.

So ist es nicht _ganz_.

Siehe fs.exists() in der Dokumentation. Es wurde durch fs.access() ersetzt

Siehe Nr. 114 für weitere Informationen.

Grund dafür: fs.exists() verwendete ein API-Format, das sich von _allem_ anderen in unserer API unterschied und Ergebnisse liefern konnte, die für diejenigen, die nichts anderes wussten, unerwartet waren.

fs.access scheint kein Ersatz zu sein, da es auch ausgelöst wird, wenn Zugriffsprüfungen fehlschlagen. Ich möchte/muss nicht jedes Mal eine Fehlerbehandlung durchführen, wenn ich nach dem Vorhandensein einer Datei suche. Ich denke, es ist nicht fair, dies als Ersatz zu bezeichnen.

Ich habe jetzt keine andere Wahl, als meinen Code entweder zu versuchen/abzufangen oder asynchron zu machen und auf Fehlerereignisse zu lauschen.

Das wäre richtig.

OK, Problem schließen, bevor ich überhaupt antworten kann? Fantastisch!

Die Ablehnung mit dieser Art von Logik/Motivation wird nur dazu dienen, diese Plattform weniger gastfreundlich für Leute zu machen, die Dinge außerhalb dessen tun, worauf sich der Kern der Funktionen konzentriert.

Ich sehe immer noch keinen Grund, warum eine gründliche Dokumentation keine angemessene Antwort auf die bisher genannten Bedenken sein sollte.

Ich bin auch ziemlich besorgt über die Erkenntnis, dass wir auf diese Weise Probleme behandeln, die um berechtigte Fragen/Bedenken herum entstanden sind.

Ein offenes Problem innerhalb von 20 Minuten nach seiner Erstellung zu unterdrücken, ist ziemlich feindselig.

Die ursprüngliche Diskussion finden Sie unter https://github.com/joyent/node/pull/8418 & #103

Ein offenes Problem innerhalb von 20 Minuten nach seiner Erstellung zu unterdrücken, ist ziemlich feindselig.

Die GitHub-Schaltfläche zum Schließen des Problems befindet sich an der gleichen Stelle wie eine Schaltfläche zum Abbrechen normalerweise. Dies ist nicht ungewöhnlich für Leute, die das gelegentlich tun. Ist die Absicht nicht klar, da ich sie direkt wieder geöffnet habe? :/

Wenn Sie Bedenken haben, für fs.accessSync() überall einen Try-Catch durchführen zu müssen, warum erstellen Sie nicht einfach Ihre eigene(n) Wrapper-Funktion(en)?:

var fs = require('fs');
function existsSync(filename) {
  try {
    fs.accessSync(filename);
    return true;
  } catch(ex) {
    return false;
  }
}
function exists(filename, cb) {
  fs.access(filename, cb);
  // or if want exactly the same old API:
 //fs.access(function(err) { cb(err ? false : true); });
}

oder warum nicht den Wrapper so lassen, wie er ist, und mich weiterhin fs.existsSync verwenden lassen? :smile_cat:

einwandfreie Funktion?

oder warum nicht den Wrapper so lassen, wie er ist, und mich weiterhin fs.existsSync verwenden lassen?

Dasselbe Denken kann auch angewendet werden, um einfach immer mehr Zucker zum Kern hinzuzufügen. Es gibt viele Diskussionen in den Themen, die mir oben gefallen haben. Zumindest war es nicht besonders perfekt, aber es war auch nicht kaputt.

Wie bereits erwähnt, bestand der _meiste Anwendungsfall_ für fs.exists() / fs.existsSync() darin, zu prüfen, ob eine Datei existiert, _bevor eine Datei geöffnet wird_. Es zu entfernen und den Leuten zu empfehlen, einfach fs.open() / fs.openSync() zu verwenden, löst die Probleme, die mit diesem Anwendungsfall der Mehrheit verbunden sind.

@Fishrock123 lass uns darüber diskutieren, wie wir es gut genug machen können, um es zu verlassen.

Soll ich eine neue Ausgabe erstellen oder können wir die Dinge besprechen, die an dieser verbessert werden müssen?

Das wurde schon zu Tode diskutiert. Umzug zum Schließen.

@bnoordhuis Wie wäre es, wenn Sie es leid sind, dieses spezielle Thema zu diskutieren: Beteiligen Sie sich nicht.

Ziemlich schwierig, wenn ich in den 30 Minuten, in denen ich nicht an meinem Computer bin, 15 neue E-Mails bekomme ... jedenfalls ist mein Punkt, dass Sie wahrscheinlich nichts ansprechen werden, was noch nie angesprochen wurde.

@bnoordhuis verständlich. Entschuldigung für den Beitrag zu Ihrem Posteingangs-Spam.

Ich werde mir die Quelle ansehen, frühere Gespräche nachlesen und sehen, was ich tun kann, um das Problem zu beheben.

Ich habe dafür ein Modul erstellt, da es immer noch echte Anwendungsfälle gibt, um zu überprüfen, ob eine Datei existiert, ohne die Datei anschließend zu lesen.

Übrigens, was ist falsch an fs.access ? fs.exists hat eine unbequeme API, zum Beispiel: https://github.com/petkaantonov/bluebird/issues/418
fs.access in Form fs.access(path, callback) scheint genau das zu tun, was fs.exists von Anfang an hätte tun sollen.
Oder übersehe ich etwas?

Ich komme nur vorbei, um zu sagen, dass ich dem Punkt zustimme, den @emilyrose hier macht.

Es stimmt, dass fs.access die Funktionen fs.exists und fs.existSync vollständig ersetzen kann. Besonders bei Wrappern wie @sindresorhus freundlicherweise implementiert. Dennoch werden viele Neueinsteiger auf der Plattform nach einer einfachen Möglichkeit suchen müssen, um boolesche Wahr/Falsch-Werte für die Dateiexistenz zu erhalten, da fs.access für sie möglicherweise nicht offensichtlich ist.

Ich persönlich denke, dass es in einigen Fällen akzeptabel ist, wenn Kern-APIs von io.js Funktionen anbieten, die Einfachheit und schnelle Entwicklung gegenüber formalen Konventionen betonen. fs.exists könnte zu dieser Kategorie gehören.

@emilyrose Der Grund, warum es veraltet ist, ist, dass fs.exists eine kaputte API hat - ich habe das Problem mit der Ablehnung geöffnet, um dies entweder zu entfernen oder zu beheben. fs.exists leidet normalerweise auch unter Race Condition, aber Sie können fs.access verwenden, um einfach das zu tun, was Sie zuvor mit exists getan haben, oder zum Teufel - verwenden Sie ein Userland-Paket, es gibt Dutzende auf NPM.

@imyller das klingt nach einem Dokumentationsproblem - und einer guten Pull-Anfrage

@benjamingr das stimmt. Ich stimme zu, dass fs.access die richtige API hat und fs.exists Probleme hat, aber ich denke, der Sinn dieses Problems ist, dass es immer noch solide Gründe dafür gibt, dass eine einfache Datei existiert oder nicht funktioniert gibt einen booleschen Wert zurück.

Außerdem möchte man vielleicht eine Dokumentation darüber, warum man eine vollständige HTTP-Server-Implementierung mit einem Einzeiler erstellen kann, während man viel mehr Zeilen benötigt, um zu prüfen, ob eine lokale Datei existiert;)

Ich denke, der Punkt dieses Problems ist, dass es immer noch gute Gründe dafür gibt, dass eine einfache Datei-existiert-oder-nicht-Funktion einen booleschen Wert zurückgibt.

Nun, Sie können mit fs.exists nicht einmal feststellen, ob eine Datei vorhanden ist oder nicht, da sie im Fehlerfall stillschweigend fehlschlägt, selbst wenn die Datei vorhanden ist. Und es tut nichts, was fs.stat nicht kann.

Ich sehe keinen einzigen Grund, warum eine solche API im Kern vorhanden sein sollte.

Darüber hinaus möchten Sie vielleicht eine Dokumentation darüber, warum Sie eine vollständige HTTP-Serverimplementierung mit einem Einzeiler erstellen können

__Keine Sorge, die gesamte http2-Community arbeitet daran. In etwa einem Jahr werden Sie keinen HTTP-Server mehr erstellen können, ohne SSL-Zertifikate zu erhalten, und es wird sicherlich kein Einzeiler mehr sein_ _

_Off-topic_: @rlidwka Nun, in einigen Monaten wird das Erhalten von SSL-Zertifikaten ein Einzeiler sein ;-).

@emilyrose hattest du Gelegenheit, dir die vorherigen Gespräche anzusehen? Wenn Sie der Meinung sind, dass zusätzliche Dokumentation hilfreich sein könnte, wäre eine PR willkommen. :)

@ Fishrock123 Ich habe! Ich habe definitiv etwas mehr Einblick in die Gründe hinter einem so starken Widerstand gegen das Halten fs.exists . Ich sehe immer noch keine gültigen Probleme mit fs.existsSync , die nicht mit Dokumentation gelöst werden können.

Ich freue mich immer noch, zu dieser Dokumentation beizutragen (und auch zu allen API-Korrekturen, die als notwendig erachtet werden), um dies zu etwas zu machen, das wir gerne behalten.

Hier ist ein einfaches Beispiel, bei dem exists() (oder genauer gesagt existsSync() ) verwendet wird, ohne dass es zu Rennbedingungen kommt:

Ich entwickle eine REST-API, bei der Ressourcen direkt von IDs auf Dateinamen abgebildet werden. Bevor ich eine CRUD-Operation durchführe, überprüfe ich, ob die Datei existiert, und sende eine "404"-Antwort, wenn dies nicht der Fall ist (es ist nicht immer erforderlich, die Datei zu öffnen, da einige der Operationen ihren Inhalt ersetzen oder löschen).

Es gibt kein Problem mit Rennbedingungen, da ich für jede Ressource eine dedizierte Ereignisschleife (implementiert als eine Warteschlange von Funktionen/Aufgaben) habe, die mir effektiv eine „Sperre“ um sie herum gibt. Auf die Dateien wird niemals von außerhalb des Knotens zugegriffen.

Wenn existsSync() aus der API entfernt wird, würde ich einfach ein Polyfill dafür schreiben, das dasselbe tut, also verstehe ich ehrlich gesagt nicht, warum das überhaupt gemacht werden sollte. Was exists() betrifft, so verstehe ich das Problem mit den nicht standardmäßigen Callback-Parametern, daher denke ich, dass dies separat behandelt werden sollte.

@rotemdan Wenn Sie existSync in Ihrem Server verwenden, würde ich argumentieren, dass Sie io.js zunächst nicht richtig verwenden, und ich frage mich, wie Ihre Leistung halbwegs vernünftig ist, da Sie die Ereignisschleife blockieren.

Außerdem _haben_ Sie eine Race-Condition - was passiert, wenn Sie Ihren Server zum Beispiel mit mehreren Kernen betreiben (und das Cluster-Modul verwenden) möchten?

@benjamingr
Die Zeiten, die ich für existSync gemessen habe, liegen normalerweise bei etwa 100 Mikrosekunden (unter Windows), wenn ich mich richtig erinnere. Es wäre äußerst selten, dass es überhaupt physische Medien erreichen müsste, um den Anruf abzuschließen, da es nur auf Metadaten zugreift. Sie könnten jedoch Recht haben, dass es bei einem stark ausgelasteten Server zu suboptimalen Verzögerungen kommen kann, aber diese sind wahrscheinlich so gering, dass sie in den meisten Fällen vernachlässigbar sind.

Es wird nicht unterstützt, dass mehrere Prozesse dieselbe Datei ändern, da derzeit keine Synchronisierung außerhalb des Prozesses erfolgt. Sie könnten denken, dass dies eine erhebliche Einschränkung ist und vielleicht sagen, dass stattdessen eine Datenbank verwendet werden sollte, aber die Sache ist die, dass die Anwendung eine Datenbank ist ! Und es funktioniert sehr gut! :)

(Ich war wahrscheinlich ungenau, es als "einfachen" Fall zu beschreiben..)

Wie auch immer, es gibt viele Fälle, in denen Single-Thread-blockierende Nicht-Server-Anwendungen (z. B. Scripting usw.) diesen Aufruf verwenden müssten, da die Leistung dort nicht wirklich ein Problem darstellt. Es gibt Situationen, in denen beispielsweise eine App eine Liste mit Dateien hat und einfach überprüfen möchte, ob sie noch vorhanden sind, und andernfalls einen Fehler ausgibt. Es wäre praktisch, eine Funktion zu haben, die genau das tut, was sie verspricht, insbesondere für unerfahrene Entwickler.

Es gibt viel schlimmere Möglichkeiten, sich "in den Fuß zu schießen", die von der API nicht wirklich verhindert werden können, z. B. das Öffnen mehrerer Instanzen einer Datei und das gleichzeitige Lesen / Schreiben darin. Sicher, die Dokumentation kann Sie immer vor den Gefahren von Dummheiten warnen, obwohl ich bezweifle, dass dies tatsächlich ihre Rolle sein sollte.

Bearbeiten: Nanosekunden -> Mikrosekunden

Vielleicht treffen wir hier einfach einen Mittelweg und verschieben die Implementierung von fs.exists() auf die Verwendung fs.access() ? Soweit ich weiß, verwenden die meisten Leute sowieso access() dafür. Auf diese Weise gibt es zumindest nahezu keinen Wartungsaufwand, und wir beschädigen niemanden.

@trevnorris Ich sehe nicht, welchen Nachteil es hat, fs.exists zu verwerfen und später zu entfernen - das Hauptproblem dabei war nicht die Race-Bedingung, sondern die inkonsistente Signatur. Die ursprüngliche Ausgabe sagt auch "deprecate or fix fs.exists".

Ich hätte nicht einmal etwas gegen fs.exists2 , wenn sich die Leute wirklich so sehr für diese Verwendung interessieren - es ist die Tatsache, dass sie eine nicht standardmäßige Rückrufkonvention hat, die mich stört und mich und viele andere Entwickler zuvor gebissen hat.

Die Tatsache, dass die Verwendung fs.exists eine schlechte Idee ist, ist ein pädagogisches Problem, und ich denke nicht, dass wir uns anmaßen sollten, die Arbeitsabläufe aller zu verstehen, aber eine inkonsistente API ist insgesamt nur eine schlechte Idee.

@benjamingr unkonventionelle Rückrufkonvention? Wie wird exists als erstes Callback-Argument übergeben?

@trevnorris Zum Beispiel petkaantonov/bluebird#418

@trevnorris Ja, fs.exists hat kein Fehlerargument als ersten Parameter seines Callbacks.

Um ehrlich zu sein, habe ich die meiste Zeit gesehen, wie Leute damit gescheitert sind, es waren keine Versprechungen oder Generatoren oder so etwas (obwohl automatische Werkzeuge ein echtes Problem sind, wenn ich darüber nachdenke). Es waren nur Leute, die mit der API nicht vertraut waren, die von der API gebissen wurden, da sie die (err, exists) -Struktur erwarteten.

@benjamingr Nur um fair zu sein, es gibt Zehntausende von Verwendungen für fs.existsSync nur auf GitHub. Und Hunderttausende von Nutzungen für fs.exists oder fs.existsSync .

_Das ist übrigens ein Beispiel dafür, wie Soft-Deprecation nicht hilft._

Genau aus diesem Grund bin ich dafür, dass es so eingestellt wird, wie es passiert ist, und nicht für seine Entfernung. Allerdings schließe ich nicht aus, eine alternative Methode mit einer korrekten Nodeback-Signatur hinzuzufügen.

Die alternative Methode mit korrekter Signatur ist fs.access , was ist daran falsch?

Allerdings schließe ich nicht aus, eine alternative Methode mit einer korrekten Nodeback-Signatur hinzuzufügen.

Ich verstehe nicht, wie exists(function(err, exists) {}) Sinn machen würde. Warum sollte das jemals ein Fehler sein?

@ Fishrock123 Weil das die Funktionssignaturkonvention ist. Siehe https://github.com/iojs/io.js/issues/1592#issuecomment -100688956 und petkaantonov/bluebird#418.

Denn das ist die Funktionssignaturkonvention.

Rechts. Da es so oder so keinen Sinn ergibt, sehe ich nicht ein, warum wir es jemals aufheben sollten.

existsSync ist eine andere Geschichte, obwohl wir möglicherweise für immer eine Abwertungswarnung für exists aufrechterhalten müssen, weil die Leute glauben könnten, dass es sie gibt.

@ Fishrock123

Es gibt meiner Meinung nach Fälle, in denen ein hypothetischer Fehler existiert _sollte_, um die ersten beiden zu nennen, die mir in den Sinn kommen:

  • Was ist, wenn die Festplatte während des Zugriffs ausgehängt wurde?
  • Es wurde ein ungültiger Pfad angegeben.

In anderen Sprachen wird es für andere Dinge ausgelöst - zum Beispiel gibt es in Java Auslöser für die Sicherheit, aber in C# nicht. Ich denke wirklich, dass die Konvention allein ein gutes Argument ist.

@benjamingr Sicher, aber man könnte argumentieren:

  1. gebe false zurück, es existiert nicht mehr.
  2. gebe false zurück, es existiert nicht.

Richtig, aber was ist, wenn es existiert, aber die Medien, auf die Sie zugreifen möchten, vorübergehend nicht verfügbar sind? Ich kann mir leicht 10 wirklich seltsame Randfälle einfallen lassen, die ein FUSE-Betriebssystem, Probleme mit der Netzwerkverbindung und so weiter betreffen, aber ich möchte wirklich nicht der Typ sein, der den seltsamen Randfall argumentiert, der in der Praxis niemanden interessiert.

Der einzige Teil, der mich wirklich an fs.exists stört, abgesehen von dem Race-Condition-Problem, ist die Tatsache, dass ich gesehen habe, wie Leute ständig davon gebissen wurden, wenn sie eine andere Signatur erwarteten.

  • Sowohl exists als auch existsSync verwenden intern bereits stat . (wäre access irgendwie besser?)
  • Das exists hat eine schlechte API, da es nicht der Callback-Konvention folgt. Es sollte verworfen werden.

Und um die Dinge ins rechte Licht zu rücken:

fs.exists = function(path, callback) {
  if (!nullCheck(path, cb)) return;
  var req = new FSReqWrap();
  req.oncomplete = cb;
  binding.stat(pathModule._makeLong(path), req);
  function cb(err, stats) {
    if (callback) callback(err ? false : true);
  }
};

fs.existsSync = function(path) {
  try {
    nullCheck(path);
    binding.stat(pathModule._makeLong(path));
    return true;
  } catch (e) {
    return false;
  }
};

Während ich an https://github.com/nodejs/node/pull/2356 arbeitete, bemerkte ich, dass fs.exists*() auch EPERM Fehler ignoriert. Dies ist ein ernstes Problem, wenn etwas in Windows beschließt, dies zu verursachen. In diesem Fall meldet fs.exists*() , dass die Datei nicht existiert, aber sie könnte tatsächlich noch existieren.. :/

Der beste Ersatz für fs.exists() ist nicht fs.access() , sondern $ fs.stat() , denn obwohl es existiert, verursacht es hässliche Verfallswarnungen und Support-Anrufe von besorgten Benutzern, aber der Zugriff auf v0 scheitert .10... Portabler Code muss beides vermeiden.

fs.exists() ~= fs.stat() ;-)

fs.exists() ~= fs.access() auch nicht!

@sam-github verwechselt die mathematische Notation. Ich meinte ~= (entspricht), nicht ¬= oder != (nicht gleichwertig). :-)

Oh, das tut mir leid!

Außer dass es im Fall von EPERM nicht der Fall ist. In diesen Fällen können Sie es tatsächlich fangen.

Ich möchte dieser Diskussion auch meine 2 Cent hinzufügen, denn obwohl es an der Oberfläche lächerlich ist, dass eine Sprache wie Node keine einfache „exists()“-Methode anbieten würde, um festzustellen, ob ein Dateisystemobjekt existiert oder nicht eine einfache boolesche Rückgabe und wir müssen so viele Entwicklungsstunden damit verschwenden, über ein so winziges Thema zu streiten, das passiert bei mir, wenn ich statSync() verwende:

try {
       var stats = fs.statSync('/path/to/file/that/does/not/exist');
} catch(e) {
      console.log('This runs, however e is undefined!');
      console.log(e);
}

und ich bekomme folgende Ausgabe:

This runs, however e is undefined!
undefined
fs.js:0
(function (exports, require, module, __filename, __dirname) { // Copyright Joy

Error: ENOENT, no such file or directory '/home/git/repos/working/56121765929e91e2193aad3b/demo/ae/system.xml'
    at Error (native)
    at Object.fs.statSync (fs.js:797:18)
........

Es scheint also, dass ich durch die Verwendung von statSync für ein Dateisystemobjekt, das nicht existiert, jetzt Fehler ausgegeben bekomme, die ich nicht abfangen kann, und hässliche Ausgaben, die ich jetzt aus dem Weg räumen muss, um damit umzugehen? zwei Daumen nach unten :( Das Überprüfen, ob etwas im Dateisystem vorhanden ist oder nicht, sollte eine sehr einfache Sache sein, und das Entfernen dieser Funktion von einem puristischen Standpunkt aus scheint verwirrend und Zeitverschwendung für alle zu sein ... Zum Beispiel:

http://stackoverflow.com/questions/4482686/check-synchronously-if-file-directory-exists-in-node-js

oder:

http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/

Sein Vorschlag ist, jetzt einen Ereignis-Listener zum Prozess hinzuzufügen, um dies zu handhaben? Ich bin verwirrt, was ist der richtige Weg, um festzustellen, ob eine Datei oder ein Verzeichnis im Dateisystem in Node vorhanden ist? Der springende Punkt beim Überprüfen, ob es existiert, liegt darin, dass die Datei möglicherweise nicht existiert und in der Lage sein sollte, dies angemessen zu handhaben. Entschuldigung, wenn ich hier etwas ganz Einfaches übersehe, aber nachdem ich diesen Thread gesehen habe, scheint es, als ob dies weiterhin ein Problem ist?

@ccravens Ich kann das nicht reproduzieren.
Was ist Ihre Version und Plattform?

RHEL 7, Knoten v0.12.4

Die Ausgabe erfolgt an console.error, nicht an console.log, wie oben angegeben. Verstehe aber immer noch nicht, warum e im catch()-Block undefiniert ist.

@ChALkeR Nachdem ich einige Nachforschungen angestellt und mir fs.js angesehen hatte, fügte ich eine process.exit() innerhalb des Fangs hinzu und erhalte nicht mehr die ENOENT-Ausgabe. Ich mache einige etwas nicht standardmäßige E / A-Verbindungen zu einem Remote-Git-Prozess mit Node und vielleicht gab es dort ein Problem mit dem Stack-Fehler, der dem Remote-Git-Client angezeigt wurde. Das ist so gelöst.

Die Ausgabe erfolgt an console.error, nicht an console.log, wie oben angegeben. Verstehe aber immer noch nicht, warum e im catch()-Block undefiniert ist.

@ccravens Bist du sicher, dass das undefined im Catch-Block ist? Versuchen Sie console.log('From catch: ' + e); statt console.log(e); .

sehr seltsam, es ist nicht mehr undefiniert, jetzt bekomme ich das:

From Catch: Error: ENOENT, no such file or directory '/home/git/repos/working/56121765929e91e2193aad3b/demo/ae/system.xml'

was ich ursprünglich vor meinem Beitrag erwartet hatte

@ccravens Mischen Sie die Ausgabe von stderr und stdout?

Während ich also an #2356 arbeitete, bemerkte ich, dass fs.exists_() auch EPERM-Fehler ignoriert. Dies ist ein ernstes Problem, wenn etwas in Windows beschließt, dies zu verursachen. In diesem Fall meldet fs.exists_(), dass die Datei nicht existiert, aber sie könnte tatsächlich noch existieren.. :/

@ Fishrock123 Ich bin mir ziemlich sicher, dass mich dieser Fehler gebissen hat. Groll zurückgenommen. :smile_cat:

@emilyrose lohnt es sich noch, das offen zu halten?

Ich möchte dieser Diskussion auch meine 2 Cent hinzufügen, denn obwohl es an der Oberfläche lächerlich ist, dass eine Sprache wie Node keine einfache „exists()“-Methode anbieten würde, um festzustellen, ob ein Dateisystemobjekt existiert oder nicht eine einfache boolesche Rückgabe und wir müssen so viele Entwicklungsstunden damit verschwenden, über ein so winziges Thema zu streiten, das passiert bei mir, wenn ich statSync() verwende:

@ccravens das ist alles in Ordnung, bis Sie anfangen, sich mit Fenstern zu befassen, die eine Ausnahme auslösen könnten, die es unmöglich macht, dies zu sagen.

Die einzig vernünftige Möglichkeit, die API unter Windows vollständig zu entbuggen, besteht darin, einen Fehler zurückzugeben, wenn es sich um EPERM handelt, was eine bahnbrechende Änderung ist, die zu groß ist, um sie imo zu rechtfertigen.

@ChALkeR Ja, ich glaube, ich habe die beiden Ausgabekonsolen verwechselt und sehe den Stack-Trace nicht mehr auf meiner Seite. Auch das Fehlerobjekt ist in der catch-Anweisung nicht mehr undefiniert. Die Funktionalität ist wie erwartet nach dem Verlassen des Prozesses über process.exit()

@ Fishrock123 Ja, einverstanden und nachdem ich einige Zeit damit verbracht habe, kann ich die Gründe dafür sehen. Ich glaube, dass das Fehlen einer "typischen"exists()-Methode mit einem booleschen Rückgabewert bei Entwicklern, die aus anderen Sprachen kommen, einige unvorbereitet trifft. Und einen vollständigen Try-Catch-Block zu konstruieren und das Ergebnis des Fehlerobjekts zu testen, scheint für diejenigen, die in Node einsteigen, eine Menge Arbeit zu sein.

Aber so viel Node mir in Bezug auf alle NPM-Pakete gegeben hat, einfache Bibliotheken, Geschwindigkeit und Flexibilität, die Vorteile überwiegen definitiv die Nachteile, keine exist() -Methode zu haben.

Ich werde das schließen. Bei Bedarf können wir wieder öffnen. Es ist im Allgemeinen eine ziemlich knifflige Angelegenheit. Zusammenfassend:

  • fs.exists(cb) entspricht nicht dem "errback" ( cb(err, ...) API-Stil, den der Kern verwendet.
  • fs.exists{Sync}() ist ein Antimuster, wenn es vor anderen fs-Operationen verwendet wird, und sollte aufgrund von Race-Bedingungen zugunsten einer Fehlerprüfung in der anderen Operation durch den Benutzer abgeraten werden.
  • fs.exists{Sync}() schlägt unter Windows vollständig fehl, wenn eine Datei aufgrund von EPERM -Fehlern nicht für den Zugriff verfügbar ist.
  • Diese API hat immer noch eine tief verwurzelte Nutzung in der Node-Community und eine Änderung (auf absehbare Zeit) ist nicht sinnvoll. Soweit ich das beurteilen kann, ist das einzig Vernünftige, was man für EPERM tun kann, es zu werfen/weiterzugeben, was Änderungen an dieser API erfordern würde.

Davon abgesehen ist die Verwendung fs.access{Sync}() nicht besonders benutzerfreundlich. Ein User-Land-Modul ist hier wahrscheinlich die beste Lösung. Für diejenigen, die eine Alternative wünschen, hat @sindresorhus hier eine: https://github.com/sindresorhus/path-exists

fs.existiert

Ein Einakter von Matthew Dean

Node: „fs.exists hat einige Probleme; am wichtigsten ist, dass es aufgrund von Race-Conditions nicht zuverlässig ist. Wir sollten es entfernen.“
Dev: "Ich habe keine Rennbedingungen."
Knoten: "Sie werden immer Rennbedingungen haben."
Dev: "Hier, sieh dir das an. Keine Rennbedingungen."
Node: "Nun, Sie haben vielleicht keine Rennbedingungen. Aber die API ist kaputt. Das ist Ihnen wichtig."
Dev: "Das tut es nicht."
Node: "Nun gut, aber es ist überflüssig, weil Sie dasselbe mit diesem anderen Ding machen können, das komplizierter und ausführlicher ist."
Dev: "Ich möchte das andere Ding nicht benutzen."
Node: "Nun, du könntest auch dieses andere Ding verwenden."
Dev: "...das ist sogar noch ausführlicher."
Node: "Nun gut, aber Sie könnten etwas von NPM verwenden."
Dev: "Warum sollte ich das tun? fs.exists existiert gerade jetzt."
Node: „Nein, das möchten Sie tun. Sie möchten es tun, weil die Dinge, die wir zum Ersetzen von fs.exists vorgeschlagen haben, komplizierter sind als fs.exists. Sie möchten auf keinen Fall die Dinge tun, die wir Ihnen vorgeschlagen haben tun."
Dev: "Warum dann fs.exists nicht einfach entfernen???"
Knoten: "Es ist zu spät. Es ist jetzt weg. Schließen als kein Problem, da das Ding, auf das es sich bezieht, möglicherweise nicht mehr existiert."
Dev: "Moment mal, existiert fs.exists noch oder nicht?"
Node: "Wir wissen es nicht. Wir haben das Ding entfernt, das Ihnen leicht sagen könnte, ob ein Ding existiert. Wir empfehlen Ihnen, etwas von npm zu installieren, das Ihnen sagt, welche Dinge in Node existieren."
Entwickler: "..."

Das Ende

Dev: "Hier, sieh dir das an. Keine Rennbedingungen."

Wenn es tatsächlich eine Race Condition gibt.

Ich bin nicht scherzhaft. Ich überprüfe viele Codebasen für meine tägliche Arbeit und fs.exists() wird meistens missbraucht.

wird häufiger missbraucht.

Ja, ich überprüfe viele Codebasen und Python wird öfter als nicht missbraucht.

(hätte auch Klassen in Java sagen können)

Ich denke, @Fishrock123 könnte sich existsSync noch einmal ansehen - die Tatsache, dass es in einem Anti-Pattern verwendet wird, bedeutet nicht, dass es keine legitimen Anwendungsfälle hat und unter Windows nicht fehlschlägt, denn wenn Sie diese EPERM-Fehler erhalten du hast sowieso ein problem. Ich denke, es gibt zwei Gründe, warum dies veraltet ist: 1) die legitime API-Diskrepanz (die nur exists betrifft) 2) sich zu sehr auf das Schreiben von Servern konzentriert, was nicht das einzige ist, wofür Node heute verwendet wird (insbesondere Desktop Skripterstellung).

Die Tatsache, dass es in einem Anti-Pattern verwendet wird, bedeutet nicht, dass es keine legitimen Anwendungsfälle gibt

Obwohl dies objektiv nicht falsch ist, ist die Tatsache, dass die Leute trotz der düsteren Warnungen in der Dokumentation die meiste Zeit immer noch falsch liegen, ein starker Hinweis darauf, dass es sich um eine schreckliche API handelt.

Für diejenigen, die sich nie wirklich mit der Implementierung exists{Sync} befasst haben, war es effektiv:

fs.existsSync = function(path) {
  return !!fs.statSync(path)
};
fs.exists = function(path, cb) {
  fs.stat(path, (err) => cb(!!err));
};

Was ich versuche zu demonstrieren, ist, dass es keine geheime Magie gibt, die fs.exists{Sync} funktioniert hat. Die Ablehnung dieser beiden Methoden bedeutet nicht, dass ein großer Teil der Funktionalität entfernt wurde. Tatsächlich handelte es sich bei ihnen schon immer um den minimalsten Zucker, der im Kern existierte.

Ich habe jetzt keine andere Wahl, als meinen Code entweder zu versuchen/abzufangen oder asynchron zu machen und auf Fehlerereignisse zu lauschen.

Wenn Ihr Code felsenfest sein soll, muss dies trotzdem geschehen. Wenn Sie dies nicht tun, kann dies funktionieren, aber es ist immer noch ein grundlegender Fehler in der Codelogik.

Es schlägt unter Windows nicht fehl, denn wenn Sie diese EPERM-Fehler erhalten, haben Sie sowieso ein Problem.

IIRC ( @Fishrock123 bitte korrigieren Sie mich) dann sollte fs.access{Sync} gut funktionieren.

@trevnorris Wenn das der Code ist, dann macht das endlich Sinn, wenn fs.exists fs.stat und das Vorhandensein eines Fehlers zurückgibt oder nicht.

Ich denke, die große Quelle der Verwirrung (zumindest für mich) war der Vorschlag, fs.open zu verwenden, um festzustellen, ob eine Datei existiert, was ebenso willkürlich ist wie die Empfehlung, dass ein Entwickler Aufrufe von fs.exists durch ersetzt Aufrufe von fs.rename oder einer anderen zufälligen Dateisystemmethode, die fehlschlagen würde, wenn eine Datei nicht existiert. Es macht keinen Sinn, wenn die Aktion, die Sie ausführen möchten, nur das Vorhandensein einer Datei bestimmt und nicht das Öffnen oder Umbenennen oder eine andere zufällige Methode ist. (Und erzeugt keine Race-Bedingung, wenn die Existenz die einzige Information ist, die abgerufen wird.)

Die Art und Weise, wie dies entfernt wurde, war also furchtbar verwirrend, wahrscheinlich weil eine Lösung, um es zu ersetzen, nicht wirklich festgelegt wurde (wie die Anzahl der Vorschläge zum Ersetzen zeigt). Außerdem war ein Teil der Erklärung, dass es nicht mit der API übereinstimmte oder normalerweise nicht wie beabsichtigt verwendet wurde oder normalerweise missbraucht wurde, was alles irrelevant und nicht hilfreich ist, also denke ich, dass dies die Diskussion ins Abseits geführt hat. Spur.

Ich denke also, dass ein großer Teil der Verwirrung in Bezug auf die Ablehnung / Entfernung dieser Methoden auf die Kommunikation zurückzuführen ist.

Kann man also mit Sicherheit sagen, dass man beim Dateizugriff immer nur feststellen kann, ob eine Datei zugänglich ist oder nicht, und nicht, ob sie existiert oder nicht? Oder meldet ein Dateisystem, dass eine Datei vorhanden ist, der Benutzer jedoch keine Zugriffsberechtigung hat? Ich versuche nur herauszufinden, welche Informationen aus der Verwendung von fs.access oder fs.stat gewonnen werden können.

Lassen Sie mich fragen, @matthew-dean, _warum_ überprüfen Sie, ob eine Datei mit einem bestimmten Namen existiert?

@benjamingr Ich habe einen meiner Meinung nach ziemlich häufigen Anwendungsfall. Ich habe in meiner App eine typische Liste mit den zuletzt verwendeten Dateien. Wenn der Benutzer die App erneut startet, verwende ich (derzeit) fs.exists , um die Liste nur schnell zu "validieren". Wenn eine Datei nicht mehr existiert, entferne ich sie aus der Liste, um die Benutzererfahrung zu verbessern. Ich habe noch ein paar andere solcher "Dateiverweise", die ich bereinige, wenn die Datei in der Zeit entfernt wurde, bevor die App wieder läuft.

Wenn/wenn der Benutzer die Datei schließlich öffnet, muss ich sicher mit der Datei umgehen, die zu diesem Zeitpunkt möglicherweise nicht vorhanden ist, aber ich müsste es trotzdem tun, da ich beim Öffnen jede Art von Dateizugriffsfehler berücksichtigen muss.

Wenn ich in meinem Fall fs.exists , ist das alles, was ich wissen möchte. Ich werde zu diesem Zeitpunkt nichts anderes mit der Datei tun. Das ist schließlich _wozu die Methode da ist_. Wenn es Zucker ist, was ist damit? Derselbe "Zucker" ist überall auf jeder Plattform vorhanden, weil es sich um nützliche Informationen handelt. zB "Ich habe ein Aktenzeichen. Ist es noch gültig?" Es wird nicht verwendet, um einer anderen Dateioperation sofort zuvorzukommen, und ich bin mir nicht sicher, warum jemand es auf diese Weise verwenden würde. Wenn es möglich ist, könnte ein neuer Entwickler von Node denken, dass er es aufrufen sollte, bevor er etwas wie das Öffnen tut, und das _könnte_ ein Problem verursachen (was, wen zum Teufel interessiert, er wahrscheinlich immer noch Dateifehler behandeln muss, einschließlich fehlender Dateifehler). , dann könnten Sie das in der Dokumentation vermerken. Aber das gilt für alle nachfolgenden Dateioperationen, bei denen sich die Statistiken einer Datei möglicherweise geändert haben, wenn Sie sie bearbeiten.

Ich meine, schließlich werden fs.watch und fs.watchFile nicht veraltet oder entfernt, obwohl in den Dokumenten steht: „Die fs.watch-API ist nicht 100 % konsistent auf allen Plattformen und in nicht verfügbar einige Situationen", was übersetzt heißt: "Vertraue niemals darauf, dass das funktioniert", und das Ansehen von Dateien über den Knoten ist bekanntermaßen so unzuverlässig, dass niemand sie jemals direkt verwendet. Die gesamte Pfad-API wird nicht entfernt, obwohl es sich hauptsächlich um Zucker für einfache Zeichenfolgenersetzungen handelt.

Also ich verstehe es nicht. Ich verstehe nicht, warum dieser, warum es so wichtig ist, sich zurückzuziehen. Warum gibt es trotz gültiger Anwendungsfälle so viel Engagement für seine Entfernung? Wenn es irgendwie scheiße ist, dann behandle es wie fs.watch und sag "das kann dich manchmal beißen". Ich möchte nicht weiter darauf herumreiten, weil ich weiß, dass es als Betreuer scheiße ist, alles im Detail erklären zu müssen, und Sie haben einen besseren Überblick über die Plattform als wahrscheinlich jeder einzelne Benutzer, aber ... das ist nur meine Meinung , Mann.

@matthew-dean fs.exists ist in keiner Weise besser als fs.access . Verwenden Sie einfach fs.access , das über eine korrekte API verfügt ( fs.exists nicht).

@ChALker Fair genug. Ich werde den Punkt nicht weiter vertiefen. Ich habe nur mehr meine persönliche Verwirrung über die Angelegenheit bemerkt, was nicht bedeutet, dass die Entfernung falsch ist; nur dass ich es persönlich nicht verstanden habe. Wenn es bei mir liegt, die Verwendung von fs.access zu verstehen, ist das auch fair. Wie gesagt, wahrscheinlich eher eine Kommunikationslücke.

IIRC ( @Fishrock123 bitte korrigieren Sie mich) dann sollte fs.access{Sync} gut funktionieren.

Sowohl stat als auch access funktionieren einwandfrei, es wird nur nicht überprüft, ob ein Fehler vorhanden ist, um anzuzeigen, ob die Datei vorhanden ist oder nicht.

Vielleicht könnten wir in Zukunft einfach exists als Wrapper über access mit einer geeigneten API oder so etwas hinzufügen. access ist zugegebenermaßen etwas komplexer zu verwenden.

Entschuldigung @ Fishrock123 , ich wollte die Diskussion nicht beenden, aber ich finde es total gut, dass ich dies schließe (wie Sie es getan haben).

@Fishrock123 access ist afaik nicht komplexer. accessSync ist.

Es gibt keinen einfachen Weg, um die Funktionalität von existsSync zu erhalten, und es ist eine so häufige Operation, dass es einfach aus dem fs -Modul herausgeschoben werden muss, das alle anderen Tools auf derselben Abstraktionsebene bereitstellt keinen Sinn. Die Dokumentation für accessSync verrät es übrigens:

Synchrone Version von fs.access. Dies löst aus, wenn Zugriffsprüfungen fehlschlagen, und tut ansonsten nichts.

Das ist eine schreckliche API, wenn ich jemals eine gesehen habe :).

@xixixao Wie unterscheidet sich das stark von existsSync ?

@trevnorrisexistsSync gibt einen booleschen Wert zurück. Das ist eine ziemlich normale Sache, die "Funktionen" tun, Werte zurückgeben. Praktischer ist es, einen booleschen Wert abhängig davon zu erhalten, ob etwas wirft, hässlich, unintuitiv und etwas, das Sie nicht jedes Mal tun werden, wenn Sie diesen booleschen Wert haben möchten, also packen Sie ihn in eine Funktion. Da Sie sich auf Node befinden, packen Sie diese Funktion in ein eigenständiges Paket, wie es bereits jemand getan hat. Das bedeutet, dass Sie diese Abhängigkeit jedes Mal verwalten müssen, wenn Sie diesen booleschen Wert benötigen. Die Frage ist, ob diese Funktionalität neben dem Rest der fs-API gehört. Prüfen, ob eine Datei existiert? Ich würde ja sagen. Das ist meine Meinung.

Nur um es schnell zu überprüfen, stellen Sie fest, dass fs.existsSync() das einfach in einen Try/Catch verpackt und entsprechend zurückgegeben hat?

Jawohl. Natürlich. Natürlich werden Sie einen Aufruf von existsSync nicht durch Folgendes ersetzen:

var fileExists = true;
try {
  fs.accessSync(filename, fs.F_OK);
} catch (e) {
  fileExists = false;
}
...

(Übrigens sagen die Zugangsdokumente nicht, was der Standardmodus ist, musste in der Quelle nachsehen ...)

Auch konnte ich in den beiden vorherigen Ausgaben keine Antwort finden: Werden diese Funktionen also aus einem Stable-Modul entfernt? Ich bin mir nicht sicher, was hier neben der Definition von praktiziert wird

Die Kompatibilität mit dem npm-Ökosystem wird nicht unterbrochen, es sei denn, dies ist absolut notwendig.

Nur um es schnell zu überprüfen, stellen Sie fest, dass fs.existsSync() das einfach in einen try/catch verpackt und entsprechend zurückgegeben hat?

Dies kann je nach Entwickler ein Argument für oder gegen eine API-Methode sein. Wie in:

Entwickler A: „Ist dir klar, dass es trivial ist, das selbst zu schreiben?“ "Richtig, das scheint unnötig zu bleiben."
Dev B: "Ist Ihnen bewusst, dass es diese Funktion gibt, die es Ihnen ermöglicht, dieses Problem weniger oft selbst zu lösen?" "Richtig, das scheint wichtig zu bleiben."

Dies kann je nach Entwickler ein Argument für oder gegen eine API-Methode sein.

Ich argumentierte nicht. Wollte mich nur vergewissern. :)

Oder sollte vielleicht eine neue Synchronisierungsfunktion die Statistiken und ansonsten null/false zurückgeben? https://github.com/nodejs/node-v0.x-archive/blob/master/lib/module.js#L86

access ist besser als exists , aber existsSync ist viel bequemer als accessSync .

Ich verwende (verwendet) existsSync in einem einfachen Shell-Skript. accessSync in einen Try/Catch-Block zu packen ist sinnlos.

Hier ist ein bescheidener Vorschlag: Heben Sie nur existsSync .

Soo ... Jeder, der bereit ist, eine existSync-Implementierung zu teilen? Ich vermute, dass try { fs.statSync(path); return true; } catch (e) { return false; } nicht ausreicht, da fs.statSync aus mehr Gründen als der nicht vorhandenen Datei wirft.

Außerdem stimme ich @dfabulich zu, warum verwerfen Sie nicht einfach fs.exists und lassen fs.existsSync ? Es scheint mir, dass alle Probleme mit der asynchronen Version zu tun haben und alle, die gegen diese Änderung sind, wollen nur eine schnelle Möglichkeit, zu überprüfen, ob eine Datei synchron existiert.

@jnordberg

fs.statSync wird aus mehr Gründen ausgelöst, als dass die Datei nicht existiert.

fs.existsSync wird aus mehr Gründen als der Datei, die nicht auf dieselbe Weise existiert, falsch zurückgeben. Das war eines der Probleme damit.

Ich möchte nur schnell überprüfen, ob eine Datei synchron vorhanden ist.

Das ist doch nicht möglich. Sie können nicht sicher zwischen einer nicht vorhandenen Datei und einer nicht zugänglichen Datei unterscheiden.

Überprüfen Sie https://github.com/nodejs/node/blob/master/lib/fs.js#L220.
Eines der größten Probleme mit exists / existsSync ist, dass der Name einen falschen Eindruck davon vermittelt, was es tatsächlich tut.

@ChALker okay, das ist schlecht. Ich habe erwartet, dass das Verhalten existSync() -> true/false oder ein Fehler ausgegeben wird, wenn es nicht bestimmt werden kann. Was halten Sie davon, die Funktion zu reparieren, anstatt sie zu entfernen?

Dadurch, dass es einen Fehler auslöst, wird der Punkt, nicht nur eine der Alternativen zu verwenden, zunichte gemacht.

Am 22. November 2015 um 9:26 Uhr schrieb Johan Nordberg [email protected] :

@ChALker okay, das ist schlecht. Ich habe erwartet, dass das Verhalten existSync() -> true/false oder ein Fehler ausgegeben wird, wenn es nicht bestimmt werden kann. Was halten Sie davon, die Funktion zu reparieren, anstatt sie zu entfernen?


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an.

@jnordberg Hast du meine Antwort gelesen? Wie ich schon sagte:

Das ist doch nicht möglich. Sie können nicht sicher zwischen einer nicht vorhandenen Datei und einer nicht zugänglichen Datei unterscheiden.

Beispiele: Datei in einem geschützten Verzeichnis (keine Möglichkeit festzustellen, ob sie nicht existiert oder ob Sie keine Berechtigung haben, darauf zuzugreifen), Probleme mit der Remote-Festplatte und der Netzwerkverbindung und so weiter.

Was sollte Ihr vorgeschlagener „fixer“ existsSync für diese Fälle zurückgeben?

Ich würde erwarten, dass es wirft. Die Frage, die wir stellen, lautet: "Existiert an diesem Ort eine Datei?"
und die Antwort kann ja oder nein lauten und ich kann es dir wegen X nicht sagen, was im Javascript-Land ein Fehler wäre.

Hier ist die Funktion, die ich im Moment verwende. Bei Interesse arbeite ich gerne an einem Patch für die fs lib.

var fs = require('fs');

existsSync.__test = (typeof fs.accessSync === 'function') ? fs.accessSync : fs.statSync;
function existsSync(path) {
    try {
        existsSync.__test(path, fs.F_OK);
        return true;
    } catch (error) {
        if (error.code === 'ENOENT') {
            return false;
        } else {
            throw error;
        }
    }
}

console.log(existsSync('.')); // true
console.log(existsSync('nonexistent/file')); // false
console.log(existsSync('/home/someone/.ssh/id_rsa')); // throws EACCES

@ Fishrock123 Ich stimme dem nicht zu, es würde nur Randfälle wie ein geschütztes Verzeichnis oder ein anderes Dateisystemproblem auslösen, über das Sie sowieso Bescheid wissen möchten. Für den normalen Gebrauch müssten Sie es nicht in einen Versuch/Fang einpacken

Es würde nur Randfälle wie ein geschütztes Verzeichnis oder ein anderes Dateisystemproblem auslösen, über das Sie sowieso Bescheid wissen möchten.

Ich habe darüber nachgedacht und bin damit einverstanden, aber dennoch ist es eine ziemlich große Veränderung, etwas zum Werfen zu bringen, insbesondere für etwas, das so weit verbreitet ist wie exists{Sync}() .

Vielleicht würde ein besserer Weg einfach einen Fehler oder wahr zurückgeben? (Dies ist immer noch eine noch größere Änderung..)

Ich würde keinen Fehler auslösen, da dies, wie Sie bereits erwähnt haben, Dinge kaputt macht und so ausführlich ist wie jede andere Methode. Ich würde es so machen wie einige PHP-Funktionen. Gibt den booleschen Wert true oder false zurück, wenn die Dinge bekannt sind, oder eine Ganzzahl (oder ein Fehlerobjekt), die einen Fehler darstellt. Dann wäre die einzige Änderung von if(fs.existsSync('file')) zu if(fs.existsSync('file') === true)

@matthew-dean Was auch den ganzen Code da draußen auf unerwartete und schwer zu bemerkende Weise kaputt machen würde. Selbst die vollständige Entfernung von fs.existsSync würde viel weniger Schaden anrichten als das, was Sie vorschlagen.

@ChALker Möglicherweise. Es ist nicht wirklich was ich will, nur eine andere Idee. Wahrscheinlich wäre es mit vorhandenem Code kompatibler, bei einem Fehler einen falschen Wert zurückzugeben.

Ich würde argumentieren, dass das Ändern von existsSync zum Auslösen von Fehlern, die zuvor geschluckt wurden, keine große Änderung ist, sondern eher eine Fehlerbehebung. Aus der Dokumentation dachte ich eigentlich, das sei das Verhalten. Ich meine, wer würde denken, dass eine Funktion, die prüft, ob eine Datei existiert, sagen würde, dass die Datei nicht existiert, wenn sie auf einen Fehler stößt?

Die asynchrone Version sollte meiner Meinung nach veraltet bleiben, es gibt keine Möglichkeit, sie zu ändern, ohne stillschweigend den Code vieler Leute zu brechen.

Ich würde argumentieren, dass das Ändern von existsSync zum Auslösen von Fehlern, die zuvor geschluckt wurden, keine große Änderung ist, sondern eher eine Fehlerbehebung.

Das würde auch viel Code kaputt machen, wie ich annehme.

@jnordberg Das Ändern von "existsSync", um zu werfen, wo zuvor Fehler verschluckt wurden, würde eine Menge Code beschädigen.

Ich versuche, an Szenarien zu denken, in denen Code brechen würde, und ich zeichne eine Lücke. Können Sie ein Beispiel geben?

https://github.com/search?l=javascript&p=96&q=existsSync&ref=searchresults&type=Code&utf8=%E2%9C%93 zeigt eine Menge Repositories, die es verwenden. Wie viele von denen würden tatsächlich von dieser Änderung abbrechen? Ich bin mir nicht sicher. Aber das wäre eine Semver-Major-Änderung

Heh, das Entfernen der Funktion würde sicher alle kaputt machen ;)

Es scheint, als gäbe es wirklich zwei Kritikpunkte an exists / existsSync . Ein Kritikpunkt war, dass exists kein Nodeback verwendet, was für existsSync kein Problem ist.

Aber das andere Problem scheint das Argument zu sein, dass exists / existsSync zu viel Sicherheit verspricht. Sie können wissen, dass eine Datei definitiv existiert, aber wenn Sie nicht darauf zugreifen können, gibt es keine Möglichkeit zu wissen, ob dies daran liegt, dass die Datei wirklich nicht existiert oder an einem anderen Problem (Sicherheit, Verbindungsprobleme usw.). access / accessSync legt die Semantik fest.

Ich halte diese Argumentation für zu vorsichtig. Keine andere Sprachlaufzeit, die ich je verwendet habe, hat sich die Mühe gemacht, mich vor diesen Grenzfällen zu schützen. Java, C#, Python, PHP, Ruby, Perl und Swift haben alle diese Probleme ignoriert und bieten einen einfachen booleschen "exists"-Test an; Ich denke, das ist auch hier die richtige Entscheidung.

Daher sollte insbesondere existsSync nicht missbilligt werden, mit Warnungen in seiner Dokumentation, dass es sich wirklich um einen Wrapper um statSync handelt und daher in seltenen Fällen die falsche Antwort zurückgeben kann.

Aber wenn Sie darauf bestehen, den Benutzer vor diesen sehr seltenen und unwichtigen Grenzfällen zu schützen, möchte ich als Skriptautor wahrscheinlich nur einen booleschen Wert, und es ist mir egal, wie die Funktion heißt.

accessSync hat jetzt eine schmerzhafte Semantik, die mich dazu zwingt, sie in einen try/catch-Block zu packen. Es ist ein sinnloser Boilerplate für einfache Skripte.

Wie wäre es also mit einer isAccessibleSync -Methode? Es wäre nur im synchronen Modus verfügbar und seine Implementierung wäre trivial:

fs.isAccessibleSync = function(path, mode) {
  try {
    this.accessSync(path, mode);
    return true;
  } catch (e) {
    return false;
  }
};

Sie könnten argumentieren, dass diese Funktion zu trivial ist, um von Nutzen zu sein, aber ich wette, dass sie ohne diese Funktion in der Standardbibliothek tausende Male geschrieben und umgeschrieben und umgeschrieben wird.

Es wäre sogar noch besser, existsSync abzulehnen, aber wenn das politisch nicht machbar ist, werfen Sie uns wenigstens einen Knochen zu und geben Sie uns _irgendeine_ synchrone Funktion, die einen booleschen Wert zurückgibt.

Ich halte diese Argumentation für zu vorsichtig. Keine andere Sprachlaufzeit, die ich je verwendet habe, hat sich die Mühe gemacht, mich vor diesen Grenzfällen zu schützen.

1.000x so viel.

Wie wäre es also mit einer isAccessibleSync-Methode?

Ich würde denken, dass accessibleSync eher "knotenartig" ist, aber ja, das auch. Wenn das Argument lautet, dass "exists" die falsche Nachricht über eine tatsächlich vorhandene Datei sendet (was meiner Meinung nach ein schwaches Argument für das Entfernen ist), dann ist es vielleicht gut, nur umzubenennen, um widerzuspiegeln, was wirklich passiert. Obwohl: Erwarten Sie, dass sich die Leute umdrehen und fragen, wie sie nur feststellen können, ob eine Datei existiert.

Oder: Deaktivieren Sie einfach existsSync . Beachten Sie die Vorbehalte in den Dokumenten zu Fehlalarmen. Stirb nicht auf diesem Hügel. Ich kann mir nicht vorstellen, dass es für das Node-Team eine große Sache ist, es einfach rückgängig zu machen, während es für eine Reihe von Entwicklern nachweislich eine große Sache ist, es entfernen zu lassen. Ob Sie einzeln zustimmen, dass es für Entwickler eine große Sache sein sollte oder _keine_ große Sache sein sollte, eine andere Methode zu verwenden, ist irrelevant. Es ist so wie es ist von Nutzen.

Hinweis: Für den Fall, dass existsSync() fehlschlägt (dh EPERM unter Windows) wird Ihr Code sowieso (seltsamerweise) fehlschlagen, also ist das Werfen eigentlich keine so verrückte Idee.

Am 23. November 2015 um 20:25 Uhr schrieb Matthew Dean [email protected] :

Ich halte diese Argumentation für zu vorsichtig. Keine andere Sprachlaufzeit, die ich je verwendet habe, hat sich die Mühe gemacht, mich vor diesen Grenzfällen zu schützen.

1.000x so viel.

Wie wäre es also mit einer isAccessibleSync-Methode?

Ich würde denken, dass accessableSync eher "knotenartig" ist, aber ja, das auch. Wenn das Argument lautet, dass "exists" die falsche Nachricht über eine tatsächlich vorhandene Datei sendet (was meiner Meinung nach ein schwaches Argument für das Entfernen ist), dann ist es vielleicht gut, nur umzubenennen, um widerzuspiegeln, was wirklich passiert. Obwohl: Erwarten Sie, dass sich die Leute umdrehen und fragen, wie sie nur feststellen können, ob eine Datei existiert.

Oder: Entwerten SieexistsSync einfach. Beachten Sie die Vorbehalte in den Dokumenten zu Fehlalarmen. Stirb nicht auf diesem Hügel. Ich kann mir nicht vorstellen, dass es für das Node-Team eine große Sache ist, es einfach rückgängig zu machen, während es für eine Reihe von Entwicklern nachweislich eine große Sache ist, es entfernen zu lassen. Ob Sie einzeln zustimmen, dass es für Entwickler eine große Sache sein sollte oder keine große Sache sein sollte, eine andere Methode zu verwenden, ist irrelevant. Es ist so wie es ist von Nutzen.


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an.

Ich möchte mich der Menge der Stimmen anschließen, die fordern, dies nicht mehr abzulehnen. File-exists-Semaphore-Mechanismen sind ein wertvolles und nützliches Werkzeug und in jedem modernen Betriebssystem extrem leistungsfähig. Dieses ganze "es ist ein Anti-Pattern, öffne es einfach" ist nur ein dummes, unhaltbares Argument. Es gibt einfach so viele triftige Gründe, warum Sie vielleicht wissen möchten, ob eine Datei existiert, ohne sie tatsächlich öffnen zu wollen. Urteilen Sie nicht darüber, wie andere Entwickler ein Problem lösen. Das ist nicht Rubin.

fs.access() funktioniert ... aber DAS ist das Anti-Pattern. Die Verwendung einer Funktion für X, die für Y entwickelt wurde, ist das, worüber sich die Leute in PHP beschweren. Geht Node dorthin?

Ehrlich gesagt geht es mir gut mit existsSync @Fishrock123 , weil die Leute es sowieso nicht für den Race-Condition-Fall verwenden würden und es scheint nützlich zu sein.

Dieses ganze "es ist ein Anti-Pattern, öffne es einfach" ist nur ein dummes, unhaltbares Argument.

Ich habe das Gefühl, dass die Anti-Pattern-Menge es bisher ganz gut verteidigt hat. :-)

Es gibt einfach so viele triftige Gründe, warum Sie vielleicht wissen möchten, ob eine Datei existiert, ohne sie tatsächlich öffnen zu wollen.

Vielleicht kannst du ein paar nennen?

@bnoordhuis if (!fs.existsSync(dir)) fs.mkdirSync(dir) ?

Ich habe das Gefühl, dass die Anti-Pattern-Menge es bisher ganz gut verteidigt hat.

Wo war das genau? Sie können feststellen, ob eine Datei existiert, indem Sie fs.utimes verwenden, denn wenn dies nicht der Fall wäre, würde es einen Fehler auslösen. Sie können feststellen, ob eine Datei existiert, indem Sie fs.rename verwenden, denn wenn die Datei nicht existiert, würde dies einen Fehler auslösen. Aber es ist ebenso willkürlich und albern zu sagen: "Wenn Sie überprüfen möchten, ob eine Datei existiert, versuchen Sie einfach, sie umzubenennen", wie zu sagen: "Wenn Sie überprüfen möchten, ob eine Datei existiert, versuchen Sie einfach, sie zu öffnen." Der Grund dafür, dass es sich um ein Anti-Pattern handelt, liegt darin, dass keine dieser anderen Dateioperationen die Absicht des Autors darstellt, obwohl sie als Nebeneffekt die gewünschten Informationen liefern können.

Vielleicht kannst du ein paar nennen?

Es gibt viele Beispiele in diesem Thread.

Seufzen. Dies ist eines der Dinge, die die Leute dazu bringen, sich darüber zu beschweren, dass es schwierig zu sein scheint, sich dieser Community zu nähern. Sie haben diese große Menge von Menschen, die darum betteln, etwas zu behalten, das für sie eindeutig wertvoll ist, und es fühlt sich an wie nur eine Steinmauer. Dieser ganze Thread ist voll von Leuten, die ihre Gründe auflisten, aber du fragst nach MEINEN? Gut, ich beiße... aber warum habe ich das Gefühl, dass es nichts bringt und ich meine Zeit verschwende?

  1. Ich habe eine Anwendung, die ein benutzerdefiniertes Bild mit einem Diagramm eines Datensatzes erstellt. Von Zeit zu Zeit kommt eine andere Anwendung und "holt" (verbraucht und löscht) die Datei. Dies ist ein Closed-Source-System, das nicht geändert werden kann. Das Verschwinden der Datei ist das Signal, eine neue zu erstellen.
  2. Ich habe ein Handelssystem, eine andere Closed-Source-App, über die ich keine Kontrolle habe, die Compliance-Management-Shutdowns über „XYZ.down“-Semaphor-Dateien meldet. Meine App soll jede Sekunde nach einer solchen Datei suchen. Klingt verrückt, aber so funktioniert es - und es funktioniert tatsächlich sehr gut, weil die Semaphoren in ein temporäres Dateisystem geschrieben werden, also sind sie sowieso alle im Speicher.
  3. Ich pflege ein altes Upload-Management-Tool, das eine Sicherheitsprüfung hat, um sicherzustellen, dass eine referenzierte temporäre Datei vorhanden ist, bevor ein externes Verarbeitungsskript aufgerufen wird, um es in ein endgültiges Speicherformat zu konvertieren.
  4. Ich habe ein Tripwire-Sicherheitssystem, von dem ein Kunde verlangt hat, dass es auf eine bestimmte Weise funktioniert. Es alarmiert einen Bediener über das Verschwinden einer beliebigen Datei aus einem Satz vordefinierter Dateien.

Nur um fair zu sein, kann jemand bitte eine Liste von Anwendungen beisteuern, bei denen diese Einstellung die Anwendung verbessert hat?

So läuft Unternehmensentwicklung ab, wenn Sie nicht die gesamte Umgebung kontrollieren, und das ist leider oft der Fall. Lieben Sie es oder hassen Sie es, es ist die reale Welt und Datei-Existenz-Tests sind eine sehr einfache und sehr verbreitete Operation.

Und ja, Sie können sie immer noch in Node ... Aber die Art und Weise, wie Sie es tun, ist UNKLAR. Ich behaupte, dass die neuen Vorschläge dem Grundprinzip widersprechen, klaren, lesbaren und wartbaren Code zu schreiben. Wenn Sie die Existenz einer Datei testen möchten, sollten Sie etwas aufrufen, das dies eindeutig tut, und nicht etwas anderes, das dies als Nebeneffekt tut.

Es gibt einen Grund, warum fast jede größere Programmier- und Shell-Skriptsprache einen File-Existent-Test hat. Das ist nicht nur syntaktischer Zucker. Nur zum Kichern habe ich meine /sbin- und /usr/sbin-Dateien gegrept und habe allein 37 Bash-Skripte, die [ -f FILE ] -Tests durchführen. Argumentieren wir, dass Node irgendwie "anders" ist? Das ist "es ist nicht für diese Art von Verwendung?"

Allein dieser Thread läuft seit Mai, und dies ist nicht der einzige Thread, in dem sich Leute über diese Richtlinie beschweren. Die gesamte Diskussion und Agita in der gesamten Community hat sicherlich den Nettosummenwert für die Entwickler-Community des "Aufräumwerts" des Verwerfens dieses Features überschritten.

Das Enttäuschendste ist, dass das einzige Argument, das ich hier gesehen habe, das besagt: „Hey Entwickler, so wird Ihr Leben durch diese Änderung BESSER“ mit der API-Konsistenz zu tun hat. Das macht mich traurig. Die Abwertung ist eine bahnbrechende Änderung – warum machen Sie sie nicht stattdessen einfach konsistent?

if (!fs.existsSync(dir)) fs.mkdirSync(dir)?

Rennbedingung. Genau aus diesem Grund denke ich, dass es gut ist, es veraltet zu lassen. Leute machen es öfter falsch als nicht.

Dies ist eines der Dinge, die die Leute dazu bringen, sich darüber zu beschweren, dass es schwierig zu sein scheint, sich dieser Community zu nähern. Sie haben diese große Menge von Menschen, die darum betteln, etwas zu behalten, das für sie eindeutig wertvoll ist, und es fühlt sich an wie nur eine Steinmauer.

Nicht nur das, sondern allein die Tatsache der Allgegenwart einer solchen Methode in anderen Sprachen (und die historische Existenz in Node) sollte ausreichen, um sie beizubehalten. Indem Sie eineexists-Methode entfernen, suggerieren Sie, dass die Node-Mitwirkenden es besser wissen als, was, alle? Irgendwie liegen alle anderen falsch? Was denken Sie, ist die Wahrscheinlichkeit dafür? Selbst wenn Sie "sehr wahrscheinlich" sagen (was erstaunlich ist), setzen Sie ziemlich stark auf die Bedürfnisse anderer Entwickler (von denen Sie zu glauben scheinen, dass sie "es falsch machen"), indem Sie die vorhandenen Methoden entfernen.

Nur um fair zu sein, kann jemand bitte eine Liste von Anwendungen beisteuern, bei denen diese Einstellung die Anwendung verbessert hat?

Ich würde auch gerne die genannten Beispiele sehen.

Ich habe das Gefühl, dass es hier zwei Argumente gibt, eines über fs.exists und eines über fs.existsSync . Um dies zu klären, habe ich eine neue Ausgabe Nr. 4216 gestartet, um speziell die synchrone Version zu diskutieren.

@dfabulich Ich habe das Gefühl, dass die beiden Argumente Pragmatismus vs. Hybris sind, aber das mag stimmen. Ich verwende wahrscheinlich immer nurexistsSync.

Zur Verdeutlichung: existsSync ist der einzige Aspekt, auf den ich auch wirklich dränge. Ich kann mich voll hinter das Argument über exists stellen und verwende es persönlich nicht.

Allerdings kaufe ich das Argument über die Race Condition nicht. Bei ALLEN dieser Methoden gibt es Rennbedingungen. access und accessSync sind dagegen nicht immun - ein externer Prozess könnte die Datei entfernen, nachdem Sie sie überprüft haben, aber bevor Sie das nächste tun. Die Überprüfung der Existenz von Dateien unterliegt Race Conditions, egal WIE Sie es tun und egal in welcher Sprache Sie es tun - Bash, PHP, Python, was auch immer. Das ist einfach die Natur des Tieres mit dieser Art von Mechanismus. Es sollte nicht die Aufgabe von Node sein, es zu "reparieren".

Allerdings kaufe ich das Argument über die Race Condition nicht. Bei ALLEN dieser Methoden gibt es Rennbedingungen.

@ crrobinson14 Es gibt keine Race-Bedingung, wenn Sie fs.mkdirSync() bedingungslos aufrufen und die EEXIST-Ausnahme behandeln, wenn sie auftritt.

@matthew-dean Bitte keine Wortspiele. Nur Fachgespräch.

@bnoordhuis Das funktioniert nur, wenn Sie das Verzeichnis tatsächlich erstellen WOLLEN. Was ist, wenn Sie nur wissen möchten, dass es nicht da ist, aber es nicht Ihre Aufgabe ist, es zu erstellen? Gleiches Problem wie bei Dateien.

Übrigens hätten alle vorgeschlagenen Lösungen die gleiche Race-Condition, wenn Sie sich wirklich darum kümmern. access , accessSync , stat , statSync - all diese könnten einen externen Akteur haben, der die untersuchte Umgebung ändert, während Sie in Ihren nächsten Schritt gehen. Nichts hier sagt, dass Sie Ihre Dateiöffnungsoperation nicht versuchen/abfangen sollten. Es ist nur eine große Menschenmenge, die sagt: "Aber ich WILL die Datei vielleicht nicht als nächsten Schritt öffnen."

@bnoordhuis Wortspiele? Es ist kein Spiel; das ist meine Wahrnehmung.

+1 zu dem, was @ crrobinson14 über die Rennbedingungen sagt. Die Rennbedingungen wurden früher angesprochen. Nicht auf alle "Existenz prüfen"-Aufrufe folgt unmittelbar eine weitere Dateioperation für diese Datei. Und die zwei Sync-Funktionen hintereinander sind keine Race-Condition in Bezug darauf, wie sie traditionell definiert wird; das heißt, zwei asynchrone Methoden in Ihrem Code, wobei die zweite von der ersten abhängt. Synchroner Code unterliegt keinen Racebedingungen. Ja, jeder externe Prozess kann eine Reihe von Dateioperationen vermasseln, aber das ist keine Race-Condition. Ein Festplattensektor, der ausfällt, nachdem Sie ihn geöffnet und mit dem Schreiben begonnen haben, ist keine Racebedingung.

Das funktioniert nur, wenn Sie das Verzeichnis tatsächlich erstellen WOLLEN.

@ crrobinson14 Was das gepostete Beispiel tun wollte. Wenn Sie nichts mit dem Verzeichnis machen, warum sollte es Sie interessieren, ob es existiert oder nicht?

access, accessSync, stat, statSync - alle diese könnten einen externen Akteur haben, der die untersuchte Umgebung ändert, während Sie mit Ihrem nächsten Schritt beginnen.

@ crrobinson14 Wenn der nächste Schritt darin besteht, das Ziel dieser Systemaufrufe zu ändern, verwenden Sie sie ja falsch.

Wortspiele? Es ist kein Spiel; das ist meine Wahrnehmung.

@matthew-dean Wörter wie "Hybris" sind geladene Sprache. Es hat hier keinen Platz.

Die Rennbedingung ist ein Ablenkungsmanöver. Natürlich gibt es Szenarien, in denen Rennbedingungen möglich oder sogar wahrscheinlich sind; aber es gibt auch viele reale Szenarien, in denen Rennbedingungen _by design_ unmöglich sind. Ein Beispiel sind Testdateien, die möglicherweise zusammen mit der Software installiert wurden, aber niemals dynamisch von der Software selbst erstellt oder gelöscht werden.

Wir haben viele Aufrufe an fs.exists in unserer Anwendung (und ein paar an fs.existsSync ), und wir hatten noch nie Race-Conditions bei ihnen. Ganz einfach, weil der Kontext, in dem sie ausgeführt werden, so ist, dass Dateien nicht gleichzeitig erstellt oder gelöscht werden (es sei denn, es läuft ein bösartiger Prozess auf dem Computer – in diesem Fall ist die gesamte Integrität unserer Software ohnehin gefährdet).

Das Ablehnen der Anrufe wird ein Problem für uns sein. Nicht so sehr für unseren eigenen Code (den wir leicht durch unsere eigene äquivalente Implementierung ersetzen können), sondern für Abhängigkeiten. Einige von ihnen werden kaputt gehen und ich bin mir nicht sicher, ob alle Abhängigkeiten rechtzeitig behoben und neu veröffentlicht werden (also müssen wir sie möglicherweise forken und unter einem anderen Namen veröffentlichen - schmerzhaft). Und manchmal verwenden wir altes Zeug für einen ganz bestimmten Zweck, es funktioniert einfach, hat immer funktioniert, und wir wollen einfach nicht das Risiko eingehen, es aufzurüsten: Wenn es nicht kaputt ist, reparieren Sie es nicht.

JavaScript hat es geschafft, sehr weit zu kommen, ohne das Web zu zerstören. Wir haben immer noch substring und substr (mit unterschiedlicher Interpretation des 2. Arguments). Dies wird die Gefühle jedes API-Puristen verletzen, aber diese APIs sind hier, um zu bleiben, ob es uns gefällt oder nicht. Selbst Methoden wie bold oder italic , die nicht dem Standard entsprechen, werden in absehbarer Zeit nicht aus Browser-Engines entfernt.

Griper könnten an meiner PR #4217 interessiert sein, die eine accessibleSync -Methode hinzufügt, die sich wie früher existsSync verhält.

@bnoordhuis In meinem Anwendungsfall ging es nicht um Verzeichnisse, sondern um Dateien, und ich habe vier klare Beispiele für Apps aus der realen Welt bereitgestellt, für die ich diese Überprüfung behalte, diese Dateien dann aber nicht öffne. Ich habe ausdrücklich erklärt, dass mein nächster Schritt in jedem Fall NICHT darin besteht, sie zu ändern.

@dfabulich Ich bin ein Fan deiner PR #4217. Nachdem ich mehr von diesen vernetzten Threads und den dort vertretenen Positionen gelesen habe, verliere ich leider die Hoffnung, dass die Core-Node-Entwickler dies akzeptieren werden.

Was ich heute gelernt habe, ist, dass sie eine Philosophie haben und daran festhalten, ob es der Community gefällt oder nicht. Gut, das kann ich respektieren - viele andere Projektbetreuer haben dasselbe getan, und ich "verstehe", dass es ein harter, oft unbelohnter Job ist.

Aber lasst uns nicht herumtanzen und so tun, als würde dies zum Wohle der Entwicklergemeinschaft getan. Es ist eindeutig nicht beliebt. Ich sehe keine Poster in diesem oder anderen Threads, die darüber sprechen, wie ihr Leben durch diese Abwertung verbessert wurde. Niemand sagt: "Ja, guter Punkt, ich werde alle meine Apps komplett ändern, weil ich jetzt sehe, dass das Aufrufen fs.access() und das Ausnutzen des Nebeneffektverhaltens von fs.F_OK , das mir sagt, dass es existiert, geht um meinen Code lesbarer oder wartbarer zu machen."

Wenn die Funktionen wegfallen, können wir das fs -Modul jederzeit mit einem Monkey-Patch versehen und wiederherstellen. Hässlich, aber pragmatisch.

Das ist mein Plan.

Hier besteht die Möglichkeit für ein sehr beliebtes Modul.

In meinem Anwendungsfall ging es nicht um Verzeichnisse, sondern um Dateien, und ich habe vier klare Beispiele für Apps aus der realen Welt bereitgestellt, nach denen ich diese Suche pflege, diese Dateien dann aber nicht öffne.

@crrobinson14 Tut mir leid, ich dachte, Ihr Kommentar steht im Zusammenhang mit dem Ausschnitt, den @jnordberg gepostet hat. Lassen Sie mich sie ansprechen.

Das Verschwinden der Datei ist das Signal, eine neue zu erstellen.

Öffnen Sie die Datei mit fs.open(filename, 'wx') , um sicherzustellen, dass die Datei entweder erstellt wird oder der Vorgang mit einem EEXIST-Fehler fehlschlägt.

Ich habe ein Handelssystem, eine andere Closed-Source-App, über die ich keine Kontrolle habe, die Compliance-Management-Shutdowns über „XYZ.down“-Semaphor-Dateien meldet. Meine App soll jede Sekunde nach einer solchen Datei suchen.

Warum nicht einfach versuchen, die Datei zu öffnen? Oder ist der Inhalt irrelevant und es geht nur um seine Existenz als ein einzelnes Bit externer Eingabe?

Das ist ein legitimer Anwendungsfall – tatsächlich der einzige legitime Anwendungsfall – aber wir haben entschieden, dass es angesichts der überwältigenden Beweise für Missbrauch Nischen genug ist, dass es sich immer noch lohnt, es abzulehnen (und weil Sie es leicht genug durch andere fs-Methoden emulieren können). )

Ich pflege ein altes Upload-Management-Tool, das eine Sicherheitsprüfung hat, um sicherzustellen, dass eine referenzierte temporäre Datei vorhanden ist, bevor ein externes Verarbeitungsskript aufgerufen wird, um es in ein endgültiges Speicherformat zu konvertieren.

Kein Urteil fällen, aber es klingt, als wäre es von Natur aus rassig.

Ich habe ein Tripwire-Sicherheitssystem, von dem ein Kunde verlangt hat, dass es auf eine bestimmte Weise funktioniert. Es alarmiert einen Bediener über das Verschwinden einer beliebigen Datei aus einem Satz vordefinierter Dateien.

Ich würde argumentieren, dass fs.access() oder fs.stat() dafür besser geeignet sind, weil sie Ihnen tatsächlich sagen, was sich geändert hat.

Danke @bnoordhuis , dass du dir alles angehört hast. Ich bin mit der Entscheidung nicht einverstanden, respektiere aber die Zeit, die Sie investiert haben, um die gesamte Diskussion auf Ihrer Seite zu verfolgen.

Wörter wie "Hybris" sind geladene Sprache.

@bnoordhuis Wenn du das sagst. Hybris kann bedeuten, dass eine bestimmte Art und Weise überlegen ist. Ein Hauptargument für diese Änderung ist nicht, dass sie nützlicher (dh pragmatischer) ist, sondern dass sie "überlegenen" Code erzeugt, der weniger "falsch" ist. Beides ist subjektiv, und viele der Hin- und Herbewegungen sind nicht darauf zurückzuführen, dass der Code des Entwicklers nicht funktioniert oder nicht das gewünschte Ergebnis liefert, sondern dass dies einfach nicht der beste Weg ist und andere Wege Missbrauch darstellen. Dies impliziert, dass der empfohlene neuere Ansatz dem alten überlegen ist, auch wenn er für den einzelnen Entwickler pragmatisch schwieriger in der Anwendung ist. Ob diese Überlegenheit auf die Philosophie zurückzuführen ist oder nicht mit anderen Node-Werten und -Zielen übereinstimmt, ist schwer zu erkennen. Es folgt nicht logisch, also ja, ich gehe wahrscheinlich davon aus, dass persönliche Voreingenommenheit der Grund sein muss, und vielleicht ist diese Annahme falsch. Offensichtlich gibt es hier Leute, die anders denken als ich. Vielleicht hätte ich Pragmatismus vs. Knotenphilosophie sagen sollen? Oder Pragmatismus vs. API-Konsistenz?

Wenn Node-Betreuer der festen Überzeugung sind, dass diese Entscheidung besser für Node ist (und das tun sie eindeutig), dann reicht das, wie bei @crrobinson14 , im Wesentlichen aus, um eine Entscheidung darauf zu stützen. Die meisten meiner Argumente waren gegen die Fälle, dass die Deprecation-Entscheidung besser für mich als Entwickler ist, oder dass andere Methoden besser zu verwenden sind, oder dass „einfach versuchen, sie zu öffnen“ genauso sinnvoll ist. Für mich ist es nicht so. Aber ich bin auch nicht jeder. Es ist frustrierend, aber ich fühle mich fertig, darüber zu streiten. ¯(°_o)/¯

Kann jemand Beispiele dafür geben, wie andere Plattformen mit exists und Zugriffsfehlern umgehen?

PHP bietet einen Durchgang zur üblichen FS-Aufrufkategorie "stat" sowie direkte Prüfungen auf bestimmte Status wie "exists". Der Aufruf gibt FALSE für nicht vorhandene Dateien und auch für Dateien zurück, auf die aufgrund von Berechtigungsproblemen nicht zugegriffen werden kann. Beachten Sie, dass es TRUE zurückgeben kann, wenn die Datei existiert, aber vom Benutzer nicht r/w/x ist. Es muss nur in der Verzeichnisliste "sichtbar" sein:
http://php.net/manual/en/function.file-exists.php

Bash bietet eine sehr große Liste von Dateitestoperationen, hauptsächlich weil es KEINEN „stat“-Aufruf bereitstellt, sodass Sie alles einzeln überprüfen müssen. Es gibt das grundlegende -e für existiert, aber es gibt auch -f für existiert-und-ist-eine-Datei, -r für existiert-lesbar usw.:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html

Beides sind offensichtlich Sync-Aufrufe. Keiner von beiden löst eine Ausnahme bei irgendeiner Art von Zugriffsfehler aus – Barrierefreiheit wird nur als separat testbarer Vorgang betrachtet. Keiner bemerkt die mögliche Auswirkung von TOCTOU auf die Sicherheit.

Java hat eine Reihe von Bibliotheken, aber die häufigste (nach meiner derzeitigen Erfahrung) ist java.nio.file.Files . Das stellt eine Methode exists zur Verfügung, die eine boolesche Existenzprüfung zurückgibt. Die Anrufe VERHALTEN sich genauso wie die anderen oben, aber die Dokumente haben Folgendes zu sagen:

Note that the result of this method is immediately outdated. If this method indicates the file exists then there is no guarantee that a subsequence access will succeed. Care should be taken when using this method in security sensitive applications.
http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#exists (java.nio.file.Path,%20java.nio.file.LinkOption...)

Dieser Aufruf löst SecurityException aus, wenn die Datei existiert, aber für den Benutzer nicht zugänglich ist. Seltsamerweise ist der verwendete Test die Lesbarkeit. Wenn die Datei beschreibbar, aber nicht lesbar ist, wird sie ausgelöst. Es ist wirklich ärgerlich, wenn Sie es mit Dingen wie Nur-Schreiben-Pipes oder Protokolldateien oder Dateisystemen mit seltsamem Verhalten zu tun haben. Die sind mir schon mal begegnet.

Python bietet den merkwürdig benannten Namen os.path.exists , der das tut, wonach er klingt, aber hauptsächlich für Dateien und nicht für Pfade verwendet wird. Python ist für plattformübergreifende Entwickler sogar noch ärgerlicher als Java - wie in der Dokumentation On some platforms, this function may return False if permission is not granted to execute os.stat() on the requested file, even if the path physically exists. vermerkt. Es verhält sich unter Windows und Unix unterschiedlich (und dies ist auch vom Dateisystem abhängig). Es werden keine Ausnahmen ausgelöst.
https://docs.python.org/2/library/os.path.html?highlight=exists#os.path.exists

Ruby bietet file.exist? als Ersatz für ein kürzlich veraltetes file.exists? . Dieser hat eine Wendung. Da steht “file exists” means that stat() or fstat() system call is successful. Das ist eine ziemlich explizite Beschreibung dessen, was tatsächlich darunter passiert - ich mag diesen Ansatz für die Dokumentation. Es zwingt die Entwickler, mehr Verantwortung für das zu übernehmen, was darunter passiert, und fühlt sich wie eine gesunde Sache an. Ruby hat eine separate Prüfung für Verzeichnisse - die anderen oben nicht.
http://ruby-doc.org/core-2.2.0/File.html#method -c-exists-3F

C# bietet system.io.File.Exists und unterscheidet auch zwischen Dateien und Verzeichnissen. Sein Verhalten stimmt mehr oder weniger mit den anderen überein, außer dass es unterwegs auch nach ungültigen Zeichenfolgen sucht und in den Dokumenten speziell eine Vielzahl von Kombinationen anmerkt, die ebenfalls False zurückgeben. Es verweist sogar auf eine separate Funktion zum Prüfen auf ungültige Pfadzeichen. Die Dokumente von C# erwähnen auch ausdrücklich die Auswirkungen auf die Sicherheit und sagen Be aware that another process can potentially do something with the file in between the time you call the Exists method and perform another operation on the file, such as Delete.
https://msdn.microsoft.com/en-us/library/system.io.file.exists (v=vs.110).aspx

Erlang bietet keine "reine" Existenzprüfung, aber is_file . Dies ist ein Kombinationstest für Dateiexistenz und -typ (kein Verzeichnis, kein Symlink). Die anderen Plattformen erforderten alle unterschiedliche Operationen, um Verzeichnis/Symlink/Datei zu überprüfen. Es wird keine Zugriffsprüfung durchgeführt.
http://erlang.org/doc/man/filelib.html#is_file -1

Mein Anwendungsfall ist, dass ich eine Datei von Pfad A in Pfad B umbenennen muss, aber nur, wenn eine Datei in Pfad B noch nicht existiert. Wenn jemand einen einfacheren Weg kennt, das zu schreiben, als eine einfache Existenzprüfung, wäre ich sehr daran interessiert zu wissen, was das ist.

Ich meine, ich werde es fs.access() oder fs.stat() versuchen, aber das liest sich nicht so aussagekräftig wie fs.exists() für die nächste Person, die meinen Code pflegen muss.

@geoffp Wenn Sie "eine Datei" sagen, meinen Sie eine gleichnamige Datei an Speicherort B? In diesem Szenario würden Sie fs.rename() aufrufen und auf EEXIST-Fehler vorbereitet sein.

Wenn es sich um eine Datei mit einem anderen Namen handelt, gibt es keine einfache Möglichkeit, dies ohne Races zu tun. (Es gibt komplizierte Möglichkeiten, dies rennenfrei zu machen, aber keine davon ist sehr portabel.)

Ich habe gestern einen neuen bekommen, der fast das gleiche macht. Ich habe einen Dienst, der niemals eine crossdomain.xml-Datei in einem bestimmten Hosting-Ordner haben kann. Wenn der Server startet, prüft er, ob es existiert. Wenn dies der Fall ist, wird es in Ruhe gelassen - der Administrator hat möglicherweise ein benutzerdefiniertes erstellt. Wenn nicht, kopiert es eine Vorlagendatei dorthin, damit zumindest ein Standard bedient werden kann.

Aber die gleiche Geschichte. Es ist falsch, nur eine Kopie/einen Fang durchzuführen, da Sie nicht kopieren möchten, es sei denn, es existiert NICHT bereits. accessibleSync ist hässlich, weil sich der Server nicht um die Zugänglichkeit kümmert. Ich habe mich vorerst für stat entschieden, aber am Ende waren es mehrere Codezeilen, wo man es getan hätte.

Ich habe jedoch einen guten Weg gefunden, dies zu umgehen. Ich habe gerade fs gestanzt, um ein existsSync zu machen, das statSync aufruft und einen entsprechenden Wert zurückgibt. Ich bin mir sicher, dass dies die Standardantwort auf Tausende von StackOverflow sein wird: "Wie überprüfe ich, ob eine Datei existiert?" Fragen in den nächsten Jahren. Es ist eine Schande, dass Core NodeJS etwas so Grundlegendes nicht kann, aber kaum das Ende der Welt ...

@ crrobinson14

Es ist falsch, nur eine Kopie/einen Fang durchzuführen, da Sie nicht kopieren möchten, es sei denn, es existiert NICHT bereits.

Ich nehme an, Sie tun Folgendes:

fs.createReadStream('defaultCrossdomain.xml').pipe(fs.createWriteStream('crossdomain.xml'));

fs.createWriteStream akzeptiert ein zweites Argument für options , in dem Sie angeben können, dass der Schreibstrom nur geöffnet wird, wenn dort keine Datei vorhanden ist, wodurch Sie die Race-Bedingung vermeiden können.

@benjamingr Nein, ich habe dies in ein Bash-Skript verschoben, das einen richtigen -f -Test hat und dies in einer einzigen Zeile mit klar lesbarem Code tun kann. Ich teilte nur mit anderen, was ich tun würde, wenn ich dies in NodeJS behalten wollte. Angesichts der jüngsten Änderungen in der Richtung von Node habe ich meine eigene persönliche Strategie geändert, um sie hauptsächlich für API-Dienste zu verwenden, die nicht viel Dateiarbeit leisten müssen. Jede Sprache hat ihren Platz und Node eignet sich hervorragend für diesen Zweck.

Ich weiß Ihren Vorschlag zu schätzen, aber der Gedanke, eine Original- / Vorlagendatei zu öffnen und zu lesen und sie dann an einen Autor weiterzuleiten, dessen Zweck darin besteht, zu scheitern, wenn das Schreiben unnötig war, ist so unglaublich unklar und ineffizient, dass ich das niemals tun würde Weg, auch wenn es der EINZIGE Weg war. Ich würde lieber exec auf ein Skript setzen, das die Arbeit richtig erledigen kann.

@ crrobinson14 Ich bin mir nicht sicher, wofür das "Nein" steht - ich habe nur darauf hingewiesen, dass es durchaus möglich ist, dies in Node ohne die Race-Bedingungen zu tun, die mit der Verwendung exists verbunden sind, und dass die Race-Bedingungen vorhanden wären wenn Sie trotzdem exists verwenden.

Sehen Sie, hier gerät diese ganze Konversation aus der Bahn. Node kann Entwickler nicht für immer vor Race Conditions schützen - es ist so einfach, sie unbeabsichtigt zu erstellen, dass es einfach albern ist, Menschen vor DIESEM zu retten. In MEINER Anwendung gibt es keine Racebedingung, da die Datei nicht bereitgestellt oder erstellt werden kann, während der Server startet. Sie sprechen eine Sorge an, die ich nie hatte und die ich nie erwähnt habe. Sie lösen also das Anliegen der NodeJS-Core-Entwickler, nicht mein Anliegen.

Deshalb habe ich die Option abgelehnt. Für mich ist es noch weniger klar als accessibleSync . Klar verständlicher Code hat für mich oberste Priorität – ich schreibe viel Code, den andere in Zukunft pflegen müssen. Keine dieser Optionen spricht diese Notwendigkeit an, ohne schwer zu sagen: "Ich mache all diese Sachen, weil sie den Einzeiler, den ich verwendet habe, als veraltet markiert haben, der genau das tat, was all diese Sachen zu tun vorgeben."

@bnoordhuis richtig, eine gleichnamige Datei. Dieser Ansatz klingt eigentlich sehr elegant, aber ich glaube nicht, dass ich das aus der Dokumentation hätte lernen können - ich habe keine Erwähnung darüber gesehen, ob das Standardverhalten von fs.rename() in diesem Fall darin besteht, ein vorhandenes zu überschreiben Datei oder einen Fehler ausgeben. Es war auch nicht offensichtlich, wenn man den NodeJS-Code oder die posix rename -Dokumentation gelesen hat.

BEARBEITEN:
Scheint nicht zu funktionieren . Das Standardverhalten von rename(2) ist das Überschreiben. Ich bin zurück zu einer manuellen Existenzprüfung mit fs.access() .

@ crrobinson14 Siehe meinen Kommentar oben, wie die Verwendung access() 's F_OK gleichbedeutend mit der Überprüfung ist, ob die Datei existiert oder nicht. Nur Zeit, die nicht unbedingt der Fall ist, ist unter Windows. In diesem Fall gibt es andere Probleme.

accessibleSync() würde also im Wesentlichen genauso funktionieren wie existsSync() , außer dass es nicht unter dem Windows-Berechtigungsproblem leidet und schneller ist.

@trevnorris Deshalb wird meine langfristige Lösung ein accessibleSync Wrapper namens existsSync sein. Wie ich oben angemerkt habe, betrifft meine Hauptbeschwerde hier die Klarheit des Codes. Wenn Sie sich Sorgen um Sicherheit und Zuverlässigkeit machen, ist Klarheit ein entscheidendes Detail, da zukünftige Betreuer einer Codebasis genau verstehen müssen, was ein Stück Code tut.

Mein Argument ist, dass Folgendes unklar ist:

fs.F_OK - File is visible to the calling process. This is useful for determining
if a file exists, but says nothing about rwx permissions. Default if no mode
is specified.

Die Verwendung von Nebeneffektverhalten zur Bestimmung der Entwicklerabsicht ist eine größere Einladung für Probleme als die anderen möglichen Probleme, die ich bisher erwähnt habe. Ich weiß gar nicht, was F_OK mir hier sagen soll - Existenz ohne Barrierefreiheit heißt "OK"?

Ich kann das Argument sehen (stimme dem nicht zu, sehe aber die Begründung dahinter), dass es einen möglichen TOCTOU-Weg hinter dem Wunsch gibt, überhaupt eine Existenzprüfung durchzuführen, unabhängig von der tatsächlichen Verwendung des Vermögenswerts. Was ich nicht sehe, ist, wie dies als ok angesehen werden kann:

// Check to see if the file exists using a side-effect of accessSync - don't worry,
// we don't actually care if we can access the file. It's just that accessSync is the
// only function that can do this because existsSync got removed... Don't even
// get me started about how this is gated off fs.F_OK...
if (fs.accessSync(..., fs.F_OK)) {

aber das kann nicht:

if (fs.existsSync(...) {

Letzteres bedarf keines Kommentars, weil klar ist, was passiert. Wenn am Ende fs.existsSync = fs.accessSync.bind(fs) funktionale Äquivalente sind, warum beenden Sie nicht einfach das ganze Argument, indem Sie ersteres als Alias ​​für letzteres bereitstellen und dokumentieren, dass es genau das tut? Schließlich ist appendFile mehr oder weniger ein Alias ​​von writeFile mit einem Flag von 'a'. Warum bieten Sie hier nicht die gleiche Nettigkeit und eliminieren die Dutzende von StackOverflow-Fragen, die sich sicherlich daraus ergeben werden?

Nur um das klarzustellen, sowohl die accessibleSync() PR #4217 als auch die „nicht veraltete existsSync “ PR #4077 sind jetzt geschlossen. Wenn einer dieser PRs nicht wiedereröffnet wird, wird sich diese Situation nie verbessern.

1) @benjamingr ist falsch, dass createWriteStream Ihnen erlaubt, den Stream nur zu öffnen, wenn die Datei nicht existiert. Das ist keine wirkliche Option. (Oder, falls ja, ist es völlig undokumentiert und im Code nicht offensichtlich sichtbar.)

2) @bnoordhuis ist falsch, dass rename EEXIST Fehler auslöst. rename ersetzt die Zieldatei. Und das kann niemals behoben werden. Alles, was der Knoten tun könnte, ist, die Existenz der Datei vorab zu überprüfen und das Rennen erneut einzuführen.

3) Soweit ich weiß, hat sich noch nie jemand erfolgreich gegen die Verwendung existsSync für die Kommunikation zwischen Prozessen (IPC) ausgesprochen. Zum Beispiel habe ich in PR #4217 das Beispiel der Überprüfung auf die Existenz von .git/MERGE_HEAD in einem Git-Hook gegeben, um zu erkennen, ob gerade eine Zusammenführung im Gange ist. Grundsätzlich ist das Dateisystem eine Möglichkeit für Prozesse, miteinander zu kommunizieren, manchmal durch Überprüfung auf das Vorhandensein von Dateien.

Hier ist es richtig, #4077 erneut zu öffnen und zusammenzuführen.

Diese TOCTOU-Diskussion erinnert mich an eine schreckliche Designentscheidung, die in den frühen Tagen von Java getroffen wurde. Es kann wie folgt zusammengefasst werden:

Lassen Sie uns alle Erfassungsmethoden als synchronized markieren. Dadurch wird verhindert, dass Entwickler sich selbst ins Knie schießen.

Das war aus zwei Gründen schrecklich:

  • Performance: Jeder Aufruf hatte den Overhead einer Synchronisation
  • falsches Gefühl der Sicherheit: Es verleitete Entwickler zu der Annahme, ihr Code sei Thread-sicher, obwohl dies nicht der Fall war. Einzelne Aufrufe waren sicher, aber Kombinationen von Aufrufen waren sehr oft noch Rennenbedingungen unterworfen => Entwickler mussten sowieso eine weitere Ebene der Synchronisation in ihren Code einfügen.

Dieser Designfehler wurde in späteren Versionen des JDK behoben. Die neuen Sammlungsklassen sind standardmäßig nicht synchronisiert (Sie können jedoch immer noch eine synchronisierte Variante erhalten, wenn Sie möchten).

Dasselbe gilt für Dateisystemoperationen (das falsche Sicherheitsgefühl, nicht das Leistungsproblem): Atomare Operationen sind frei von Race-Conditions, Operationen, die mehrere atomare Operationen kombinieren, sind es jedoch normalerweise nicht. Um auf der sicheren Seite zu sein, benötigen Sie einen übergeordneten Mechanismus (irgendwo eine Art Sperre, einen sperrenfreien Algorithmus, einen Wechsel in den _exklusiven_ Modus, ...).

Das Verwerfen fs.exists ist also sehr ähnlich wie das Verwerfen list.isEmpty . Natürlich sind diese Dinge in gleichzeitigen Kontexten von Natur aus unsicher. Aber sie sind vollkommen legitim (und ausdrucksstark) in Kontexten, in denen Nebenläufigkeitsbedingungen auf andere Weise eliminiert wurden.

Ich werfe dies auf die Tagesordnung des CTC-Meetings und werde alle hier gegebenen Rückmeldungen zur weiteren Diskussion aufnehmen.

Wiedereröffnung, damit ich es nicht vergesse.

:+1:

it's happening

Python bietet den merkwürdig benannten Namen os.path.exists , der das tut, wonach er klingt, aber hauptsächlich für Dateien und nicht für Pfade verwendet wird. Python ist für plattformübergreifende Entwickler sogar noch ärgerlicher als Java - wie in der Dokumentation On some platforms, this function may return False if permission is not granted to execute os.stat() on the requested file, even if the path physically exists. vermerkt. Es verhält sich unter Windows und Unix unterschiedlich (und dies ist auch vom Dateisystem abhängig). Es werden keine Ausnahmen ausgelöst.
https://docs.python.org/2/library/os.path.html?highlight=exists#os.path.exists

@ crrobinson14 Das klingt wie unser aktuelles existsSync() . Das heißt, es wird mehrdeutig, wenn die Datei existiert, aber nicht zugänglich ist. Daran führt kein Weg vorbei, also war unser logischer Weg, accessibleSync() vorzuschlagen.

Idealerweise würde fs.existsSync() nur access verwenden, aber das ist aus Gründen, die jetzt möglicherweise auf Zeit verloren gehen, nicht möglich, wie in https://github.com/nodejs/node/pull/4217#issuecomment - 168811245. (Wenn Sie darüber sehr besorgt sind, können Sie gerne versuchen, diese Gespräche auszugraben, aber ich denke nicht, dass dies eine effektive Nutzung unserer Zeit ist. :/)

Letzteres bedarf keines Kommentars, weil klar ist, was passiert. Wenn am Ende fs.existsSync = fs.accessSync.bind(fs) funktionale Äquivalente sind, warum beenden Sie nicht einfach das ganze Argument, indem Sie ersteres als Alias ​​für letzteres bereitstellen und dokumentieren, dass dies das ist, was es tut?

Siehe oben. :/


Mein Anwendungsfall ist, dass ich eine Datei von Pfad A in Pfad B umbenennen muss, aber nur, wenn eine Datei in Pfad B noch nicht existiert. Wenn jemand einen einfacheren Weg kennt, das zu schreiben, als eine einfache Existenzprüfung, wäre ich sehr daran interessiert zu wissen, was das ist.

Soweit ich das beurteilen kann, ist dies ohne einen access - oder stat -Check nicht lösbar, da rename(2) standardmäßig überschreibt und keine Option hat, dies nicht zu tun.

Nur um das klarzustellen, sowohl die accessibleSync() PR #4217 als auch die „nicht veraltete existsSync “ PR #4077 sind jetzt geschlossen.

https://github.com/nodejs/node/pull/4217 wurde nie geschlossen und ist wahrscheinlich die praktikabelste Option, obwohl ich sicher bin, dass nicht jeder 100 % zufrieden damit sein wird, müssen wir mit dem arbeiten, was wir können. (dh wir können nicht einfach die Semantik fs.existsSync() ändern (brechen).)

Dieses Problem wurde heute beim CTC-Call besprochen. Wir konnten uns darüber nicht vollständig einigen, aber es wurde beschlossen, uns die Abwertungsstrategie noch einmal anzusehen und zu sehen, ob wir die Art und Weise der Abwertung verfeinern können. Wir werden auch die verschiedenen Probleme speziell rund um existSync besprechen und sind uns einig, dass eine bessere Lösung entwickelt werden muss, als einfach zu sagen „Tu das nicht“. Da wir bei dem Anruf heute jedoch zu keiner Lösung kommen konnten, haben wir uns entschieden, das Gespräch auf Github zurückzusetzen, um zu sehen, ob wir zu einer Lösung kommen können.

Soweit ich das beurteilen kann, ist dies ohne Zugriffs- oder Statistikprüfung nicht lösbar, da rename(2) standardmäßig überschreibt und keine Option hat, dies nicht zu tun.

Entschuldigung, ich habe die Kommentare zu rename(2) verpasst. @dfabulich hat Recht, dass es das Ziel überschreibt, aber Sie können dies umgehen, indem Sie den alten Pfad fest mit dem neuen Pfad verknüpfen (was mit EEXIST fehlschlägt, wenn bereits eine Datei mit diesem Namen vorhanden ist) und dann die Verknüpfung des alten Pfads aufheben. Dh:

try {
  fs.linkSync(oldPath, newPath);
  fs.unlink(oldPath);
} catch (e) {
  if (e.code == 'EEXIST') {
    // handle error
  }
}

Vorsichtsmaßnahme: kein Ton bei NFS-Mounts, aber rename() auch nicht.

@bnoordhuis Das verschiebt nur die Rennbedingung. Während Sie den Hardlink erstellt haben, könnte etwas mit dem _alten_ Pfad passieren, was dazu führen könnte, dass der unlink -Aufruf fehlschlägt (oder noch schlimmer, etwas löschen, was Sie nicht löschen wollten).

Sicher, aber die ursprüngliche Frage war, wie man nur umbenennt, wenn das Ziel nicht existiert, keine Einschränkungen für die Quelle. Der obige Code ist solide, solange alle Akteure eine atomare Create-or-Fail-Ownership-Logik für Quelle und Ziel verwenden.

Konnte jemand die alte Datei nicht erfolgreich aufheben und sie durch etwas Wichtiges zwischen dem Link/Unlink in Ihrem Beispiel ersetzen?

@dfabulich Ja, aber dasselbe ist auch mit exists* möglich.

Tut mir leid, wenn sich das wiederholt, aber dieser Thread ist viel zu lang, als dass ich ihn vollständig lesen könnte :)

Ich verwende (gut verwendet) die Funktion fs.existsSync zum synchronen Überprüfen UND synchronen Lesen von Konfigurationsdateien beim App-Start und in Cron-Dateien, wenn Bash nicht richtig abgespielt wird. Hier sind meine 2 Cent dafür, was wahrscheinlich 1 Cent wert ist, wenn man bedenkt, dass ich nicht den gesamten Thread gelesen habe:

  • Das Dumping, aber auch das Bereitstellen eines Ersatzes ist in Ordnung, aber das Ersetzen eines zurückgegebenen booleschen Fehlers durch einen ausgelösten Fehler ist es nicht. Wenn fs.accessSync() und fs.statSync() falsch zurückgegeben würden, würde der folgende Code mit beiden verwendeten Funktionen funktionieren:
if (!fs.existsSync('/path/to/nowhere') && !fs.accessSync('/path/to/nowhere') && !fs.statSync('/path/to/nowhere') {
  console.log('It REALLY doesn\'t exist');
}
  • In meinem Fall verwende ich fs.existsSync() , um nach einer Konfigurationsdatei zu suchen und sie dann synchron einzulesen. Leute haben sich darüber beschwert, dass _jemand_ die Konfigurationsdatei nach der Synchronisierungsprüfung und vor dem Synchronisierungslesen ändern/löschen/was auch immer ändern könnte. Zugegeben, ja, das könnte eine Möglichkeit sein, aber wenn Sie sich darüber Sorgen machen, dann schlagen Sie einige Dateiberechtigungen auf Ihre App oder sichern Sie Ihre Box und das Problem ist gelöst, was Sie ehrlich gesagt sowieso tun sollten (vertrauen Sie niemandem).

Abgesehen davon kann ich es mit einem Versuch/Fang umgehen oder ein Modul wie path-exists verwenden (jetzt so viele Module), also ist das Leben nicht so schlimm, aber dieses veraltete Flag in den Dokumenten zu sehen, war wie das Aufwachen und sich an einem kalten Morgen den Zeh anstoßen. Ärgerlich, aber du wirst leben

PS: Ich schlage nicht vor, fs.accessSync() und fs.statSync() zu ändern, sonst gibt es einen weiteren Thread :)

Etikett ctc-agenda entfernt, kann es wieder eingeschaltet werden, wenn dies nirgendwo hinführt und wir erneut nach einem Leistungsschalter suchen müssen

Wir wurden gerade von dem anderen Problem gebissen, dass fs.existsSync für eine vorhandene Datei false zurückgibt, wenn dazwischen Hardlinks verwendet werden. Das ist etwas, das ich viel ernster finde als eine Nicht-Standard-Syntax.

Hier ist ein Anwendungsfall, bei dem ein einfacher boolescher Rückgabewert sinnvoll ist und die Überprüfung auf Fehler nicht:

Promise.join(fs.access('path'), somethingAsync(), anotherAsync())
.then(...)
.catch(...);

In diesem Beispiel mit bluebird wird, wenn path nicht existiert, ein Fehler ausgegeben und die andere then -Klausel wird nie ausgeführt, stattdessen erhalten Sie catch . In dem Fall, in dem Race-Conditions kein Problem darstellen (in meinem Fall überprüfe ich nur, ob ein Stylesheet existiert, und füge es hinzu, wenn es existiert), sehe ich keinen Grund, warum dies nicht erlaubt sein sollte. Eine einfache Warnung in der Dokumentation, fs.exists nicht zu verwenden, bevor eine Datei gelesen wird, sollte ausreichen.

Bearbeiten: Das betreffende Stylesheet wird zur Build-Zeit von einem Makefile erstellt

Sie verwenden Promise.join falsch und könnten .reflect verwenden, um Ihre Beschwerde zu lösen.

Wie ist es eine falsche Verwendung? Es gibt eine Reihe von Werten aus Datenbank, Dateisystem usw., auf die ich vor dem Rendern warte, was mir absolut sinnvoll erscheint.
Auch Reflect löst das Problem nicht vollständig, denn was ist, wenn ein echter Fehler (dh kein "Datei existiert nicht" ENOENT -Fehler) geworfen wird? In diesem Fall möchten Sie, dass die catch-Klausel ausgeführt wird, aber aufgrund von Reflect wird dies nicht der Fall sein.

Nein, ich meine buchstäblich, dass Sie join nicht ohne Rückruf verwenden sollten - das funktioniert nur für die Abwärtskompatibilität. Schauen Sie sich die Dokumente an - Sie können .all verwenden.

Sie können reflect und den Wert überprüfen oder dieses Versprechen direkt .fangen und Petkas Fehlerprädikat-Helfer verwenden, um enoent zu ignorieren - sollte einfach genug sein.

Oh verdammt, ich habe diese spezifische API-Änderung nicht bemerkt, ich denke, ich werde später aktualisieren.
In jedem Fall können Sie natürlich einen Wrapper wie den von mscdex ganz nach oben im Thread schreiben oder den Wert manuell mit reflect überprüfen, kein Problem, aber semantisch gesehen wäre es nicht am sinnvollsten, einfach true zurückzugeben false in diesem Fall? Ich versuche nur darauf hinzuweisen, dass es echte Anwendungsfälle für eine asynchrone exists -Methode gibt, der obige scheint einer davon zu sein, alles andere fühlt sich wie ein unnötiger Hack an

Auch in Ihrem Code würde es nicht funktionieren, da Sie zuerst promisifyAll fs und dann accessAsync verwenden müssten. Das wäre ohnehin für fs.exists kaputt gegangen, weil es nie einen Rückruf im Node-Back-Stil benötigte.

Konzentrieren wir uns jedoch auf Ihren Fall - was ist, wenn der Build das Stylesheet erstellt oder entfernt, nachdem Sie es überprüft und bevor Sie es eingefügt haben? Wäre es nicht besser, das Stylesheet _trotzdem_ einzubinden und Fehler dort zu behandeln?

Hm ... hier mit exists die Abwärtskompatibilität zu brechen und daraus einen Node-Back-Callback zu machen, wäre doch sinnvoll, oder? Vor allem, da es schon seit langem veraltet ist. Andernfalls könnte die oben vorgeschlagene Methode fs.accessible ein Callback im Node-Back-Stil sein.

In meinem speziellen Fall würde der Befehl make angeblich zur Build-Zeit ausgeführt, bevor der Server gestartet wird, und wenn er jemals wieder ausgeführt wird, nach dem Stoppen des Servers und dem Ausführen make clean . Wenn Sie also diesem Workflow folgen, würde Ihr hypothetischer Fall niemals eintreten. Das Einschließen des Stylesheets würde trotzdem zu einem unnötigen 404 führen. Insbesondere möchte ich ein sprachspezifisches Stylesheet in eine i18n-Umgebung einschließen, aber nicht alle Sprachen haben spezielle Stile.
Gibt es etwas, was ich dann vermisse?

Hm ... die Abwärtskompatibilität hier mit exist zu brechen und daraus einen Callback im Node-Back-Stil zu machen, wäre doch sinnvoll, oder?

Das würde _viel_ existierenden Code kaputt machen, nur weil wir exists mit einem netten "veraltet"-Sticker versehen haben, heißt das nicht, dass die Leute ihn nicht mehr benutzen. Es würde viel Code brechen.

Ja, ich kenne keine Methoden für diese Art von Einstellungen ... Aber wenn es lange genug veraltet ist, können Sie es schließlich entfernen oder ändern, oder?

@benjamingr @perrin4869
Warum es nicht einfach mit beiden Welten funktionieren lassen.

Diese Lösung funktioniert gut, wenn sie mit callback(exists) und callback(err, exists) aufgerufen wird.

Legacy-Code, der einen Callback mit booleschen Argumenten erwartet, wird nicht unterbrochen, und Code, der ihn im Nodeback-Stil aufruft, würde ebenfalls das erwartete Ergebnis erhalten.

Dies funktioniert unter der Annahme, dass der Nodeback-Stil mehr als ein Funktionsargument hat - wie immer - und der Legacy-Stil nur eines hat.

Offensichtlich gehen alle Fehlerdetails von fs.stat verloren, aber ist das nicht der Sinn von fs.exists ?

fs.exists = function (path, callback) {
    if (!nullCheck(path, cb)) return;
    var req = new FSReqWrap();
    req.oncomplete = cb;
    binding.stat(pathModule._makeLong(path), req);
    function cb(err, stats) {
        if (callback) {
            if (callback.length > 1)
                callback(null, err ? false : true);
            else
                callback(err ? false : true);
        }
    }
};

@imyller Cool! Fehlerdetails sind in der Tat irrelevant :)

Übrigens, nur als Nebenbemerkung, ich war neugierig, also habe ich Folgendes überprüft: Ich habe gerade das node_modules -Verzeichnis meines Projekts durchgesehen, installiert mit npm@3 und [email protected] , es hat 911 verschiedene Repositories . Ich habe die folgenden Befehle ausgeführt:

$ ag 'fs.exists' -l | wc -l                                                                                                                                          
133
$ ag 'fs.exists\(' -l | wc -l                                                                                                                                        
29
$ ag 'fs.exists\(' -l                                                                                                                                                
write/index.js
pmx/lib/probes/profiling.js
gulp-symlink/index.js
coffee-script/lib/coffee-script/command.js
node-sass/scripts/build.js
fs-extra/lib/json/output-json.js
fs-extra/lib/ensure/file.js
fs-extra/lib/ensure/symlink.js
fs-extra/lib/ensure/link.js
fs-extra/lib/copy/copy.js
fs-extra/lib/output/index.js
fs-extra/lib/ensure/symlink-paths.js
find-up/node_modules/path-exists/readme.md
jspm/lib/install.js
jspm/lib/package.js
mkdirp/test/opts_fs.js
mkdirp/test/opts_fs_sync.js
jspm-npm/lib/node-conversion.js
phantomjs/lib/phantom/examples/scandir.js
phantomjs/lib/phantom/examples/scandir.coffee
express-winston/node_modules/winston/lib/winston/transports/file.js
systemjs-builder/lib/trace.js
vizion/lib/identify.js
winston/lib/winston/transports/file.js
path-exists/readme.md
jspm-github/github.js
rmdir/Readme.md
rmdir/lib/rmdir.js
pm2/lib/tools/VersionManagement.js

Wie Sie sehen können, gehen die meisten Aufrufe tatsächlich an fs.existsSync , mit nur 29 Verweisen auf fs.exists . Nicht besonders bedeutsam, oder?

Ähm...

Das würde eine Menge existierenden Codes kaputt machen, nur weil wir einen netten "veraltet"-Sticker auf Existens kleben, heißt das nicht, dass die Leute ihn nicht mehr benutzen. Es würde viel Code brechen.

Aber ... Sie entfernen es vollständig ... Wie ist das besser?

@ crrobinson14 niemand entfernt irgendetwas vollständig von irgendwo. fs.exists wird für alle Ewigkeit in node weiter existieren - es gibt keine Möglichkeit, es in den nächsten 5-10 Jahren realistisch zu entfernen, ohne das Ökosystem zu zerstören.

@benjamingr Dieser ganze Thread und der damit verbundene Aufruhr entstanden aufgrund der Ablehnung von fs.exists und fs.existsSync. Beide sind als veraltet markiert.

Veraltung != Entfernung.

fs.access ist "Testet die Berechtigungen eines Benutzers für die durch den Pfad angegebene Datei"

Ich möchte überprüfen, ob die Datei vorhanden ist, nicht, ob der Benutzer Berechtigungen für die Datei hat.

Grund: Ein Blog, das Dateispeicherung mit „post_title.md“ als Dateinamen verwendet. Wenn ich einen neuen Beitrag mit einem CLI-Tool erstelle, möchte ich zuerst prüfen, ob dieser Titel bereits verwendet wird, indem ich einfach überprüfe, ob die Datei vorhanden ist.

Keine Race-Bedingungen, keine Notwendigkeit, die Datei zu öffnen oder zu prüfen, ob der Benutzer Berechtigungen dafür hat.

Bitte bringen Sie fs.exists zurück.

@felixsanz Was ist falsch an fs.open(filename, 'wx') und der Behandlung des EEXIST-Fehlers? Wenn Ihr Argument "es ist mehr Arbeit" lautet, lautet mein Gegenargument "es ist viel robuster".

@bnoordhuis Weil fs.open für "eine Datei öffnen" selbsterklärend ist und ... ich es einfach nicht öffnen möchte. Nicht einmal bei weitem.

Existenz prüfen !== öffnen

Wissen Sie, wenn Sie die Dokumentation für Ihre "exists"-Methode durchsuchen, werden Sie nie denken, dass "fs.open" diejenige ist, die Sie brauchen. Aus diesem Grund wird es beispielsweise neue Entwickler verwirren. Das Fehlen einer Existiert-Methode ist meiner Meinung nach eine Lücke in der FS-API. Wenn es kaputt war, reparieren Sie es einfach. Umgehen Sie es nicht mit Methoden, die nichts zu tun haben, wie "fs.open" (eine Datei öffnen) oder "fs.access" (auf Benutzerberechtigungen prüfen).

Sicher, Sie können die gleiche Funktionalität erreichen, aber das ist nicht so, wie es sein soll. Sie können auch Strings für Datumsangaben verwenden, aber dafür gibt es das Date-Objekt. Verspotten Sie nicht die Funktionalität ...

Der einzige Nachteil, den ich bei der Implementierung meiner vorgeschlagenen Lösung sehe, ist, dass sie die Verwendung von bereits veraltetem fs.exists fördern könnte. "inkompatible" Callback-Funktionsparameter waren nicht der einzige Grund für die Ablehnung fs.exists ; Es gibt auch interne Implementierungsprobleme im Zusammenhang mit Dateisystemen und Plattformportabilität.

Ich freue mich jedoch, eine PR für diese Funktion zu erstellen, wenn genügend Unterstützung vorhanden ist und es als wünschenswert angesehen wird, abwärtskompatible Funktionen zu bereits veralteten Funktionen hinzuzufügen.

@imyller , ich würde es vorziehen, wenn die Leute Ihren Code einfach dort verwenden, wo sie ihn wollen, und dies nicht als Teil des Node.JS-Kerns aufnehmen.

@GeorgeBailey Das war eine geringfügige Änderung der aktuellen fs.exists -Funktion von lib/fs.js im Node.js-Kern. So wie es ist, ist es nicht als eigenständige Lösung verwendbar, da es sich auf private Hilfsfunktionen innerhalb der fs -Bibliothek bezieht.

[zusammengeführt in einem früheren Kommentar] Ich habe erlebt, wo einzelne Funktionen (wie Java separate Funktionen für isFile , exists , length usw. hatte) den Leistungseinbruch verschleiern ein unerfahrener Entwickler. Ich kann sehen, dass jemand nacheinander sowohl exists als auch stat anruft, was eine Verschwendung wäre.

Außerdem mag ich es wirklich, den Node.JS-Kern schlicht und einfach zu halten. Konsistenz ist dabei ein wichtiger Bestandteil.

Wenn ich einen neuen Beitrag mit einem CLI-Tool erstelle, möchte ich zuerst prüfen, ob dieser Titel bereits verwendet wird, indem ich einfach überprüfe, ob die Datei vorhanden ist.

@felixsanz , für Ihren Anwendungsfall sollten Sie stat verwenden, nicht open .

Wissen Sie, wenn Sie die Dokumentation für Ihre "exists"-Methode durchsuchen, werden Sie nie denken, dass "fs.open" diejenige ist, die Sie brauchen. Aus diesem Grund wird es beispielsweise neue Entwickler verwirren. Das Fehlen einer Existiert-Methode ist meiner Meinung nach eine Lücke in der FS-API.

Also ja, Sie würden nicht open verwenden, sondern stattdessen stat . Ich verstehe, was Sie meinen, dass die Leute dies vielleicht nicht wissen, aber denken Sie daran, dass exists als bequeme Methode angesehen werden könnte, da es sowieso stat macht. (Dies gilt für alle Programmiersprachen.) Daher würde ich vorschlagen, dass die Leute stat anstelle von exists verwenden, damit sie wissen, was sie tun, und verstehen, dass die Auswirkung auf die Leistung dieselbe ist. Das gilt eigentlich auch für file size , isDirectory usw.

Eine einfache Suche im Internet wird Ihnen sagen, dass stat die Antwort auf all diese Fragen ist.

Wenn Sie den Komfort zurückhaben möchten, können Sie eine Convenience-Methode erstellen.

Wenn die Lösung von @imyller im Node.JS-Kern implementiert wird, gibt sie Ihnen das, wonach Sie suchen, aber es macht es umso schwieriger, die Inkonsistenz zu beseitigen, die dazu führte, dass sie überhaupt nicht mehr empfohlen wurde.

@GeorgeBailey , also sollten die Leute jedes Mal in ihren Projekten eine Bequemlichkeit fs.exists neu implementieren? Es scheint mir eine Methode zu sein, die grundlegend genug ist, um sie nicht jedes Mal neu implementieren zu müssen

@GeorgeBailey

Wenn Sie den Komfort zurückhaben möchten, können Sie eine Convenience-Methode erstellen.

Wir könnten auch console.log aus der API entfernen, wenn jemand es braucht, können Sie dies implementieren:

console.log = function (x) {
  process.stdout.write(x + '\n');
};

Und wenn Sie es nicht implementieren möchten, verwenden Sie eine NPM-Bibliothek!

Sorry für die Ironie, aber es war ein Beispiel.

Ich habe PR #5098 für meine abwärtskompatible Lösung erstellt, um Callback im Nodeback-Stil mit fs.exists zu ermöglichen.

Offensichtlich löst dies keine anderen dateisystem- oder plattformbezogenen Probleme mit fs.exists , behebt aber das unkonventionelle Callback-Problem.

@felixsanz , ich schätze dieses Beispiel nicht. console.log leidet nicht unter ähnlichen Inkonsistenzen, die dazu führten, dass fs.exists als veraltet markiert wurde. (Sie wissen, dass es eine Inkonsistenz gibt, oder?) Außerdem enthält console.log zusätzliche Funktionen, die util.inspect verwenden, daher wäre es schwieriger, eine praktische Methode dafür zu erstellen.

fs.exists ist ein einfaches Problem. Schauen wir uns nur als Referenz den Unterschied im Code an.

fs.exists(filename, function (exists) { // old format
    // when there is an error (i.e. no permission), it just sets exists to false
    var existing = exists
})
fs.exists(filename, function (err, exists) { // new format
    if(err)
        // an error
    else
        var existing = exists
})
fs.stat(filename, function (err, stats) {
    if(err && err.code != 'ENOENT')
        // an error
    else
        var existing = !!stats
})

Auf der einen Seite ist also der Unterschied im benötigten Code gering, und die Möglichkeit, Kompatibilität in beide Richtungen zu implementieren, ist bereits in der Pull-Anforderung von @imyller enthalten . Wir können glücklich sein, egal in welche Richtung es geht.

fs.existsSync ist ein schwierigeres Problem.

var existing = fs.existsSync(filename) // old format
// when there is an error (i.e. no permission), it just sets returns false

var existing = fs.existsSync(filename) // new format
// may throw an error

try {
    var existing = !!fs.statSync(filename)
} catch (e) {
    if(e.code != 'ENOENT') throw err
}

Ich kann also verstehen, warum die Leute statSync nicht verwenden möchten, weil catch selbst für den normalen Betrieb notwendig wird. Aber wenn wir weiterhin existsSync verwenden, gibt es keine Möglichkeit, beide Formate zu unterstützen. Entweder führen wir das neue Format ein (um mit dem Rest der fs -API konsistent zu sein), was eine bahnbrechende Änderung darstellt, oder wir fahren auf unbestimmte Zeit mit dem alten Format fort (bereits vorhanden).

@perrin4869 , @felixsanz , was denkst du, sollten wir tun?

Persönlich bin ich offen für die Idee, fs.exists so einzurichten, dass es im neuen Format funktioniert, aber ich denke, dass fs.existsSync vollständig eingestellt werden muss, bevor es im neuen Format eingeführt wird. Mir ist klar, dass das mehrere Jahre dauern würde / wahrscheinlich 2 oder 3 Hauptversionen. Aber ich mag die Alternative nicht, einfach existsSync für immer im alten Format zu behalten, weil es falsch zurückgibt, wenn es einen Fehler werfen sollte.

Wenn man sich die PR-Notizen ansieht, hat die Lösung von @imyller einige unbeabsichtigte Konsequenzen, außerdem kann sie existsSync nicht verarbeiten. Ich denke, dass uns nur zwei Möglichkeiten bleiben

  1. Lassen Sie fs.exists und fs.existsSync auf unbestimmte Zeit in Ruhe. (inkonsistent mit dem Rest der fs API)
  2. Bauen Sie es mit der Zeit aus. (Abkündigungshinweis in der nächsten Hauptversion (oder zwei), gefolgt von vollständiger Entfernung für mindestens eine ganze Hauptversion)

Ich bin für Nummer 2, und dann können wir es richtig zurückbringen, wenn noch Lust darauf besteht.

@GeorgeBailey Bist du mit meiner PR Nr. 4217 auf dem Laufenden? Ich behaupte dort, dass das synchrone No-Errors-Format viel brauchbarer ist.

fs.accessSync wirft Fehler auf die Art und Weise, wie Sie denken, dass fs.existsSync es tun sollte, und das macht es wirklich schmerzhaft, damit zu arbeiten. Es ist unmöglich, eine einfache if -Anweisung zu schreiben, z. B. if (fs.accessSync(file)) , denn wenn die Datei nicht existiert, wird sie ausgelöst, sodass Sie die Ausnahme abfangen müssen (und sie dann höchstwahrscheinlich ignorieren). .

Ich denke, fs.existsSync sollte nicht veraltet sein, und dass fs.exists hier die einzige Funktion mit einem echten Problem ist und zu Recht zugunsten von fs.access abgelehnt wurde, das Nodeback verwendet, ist gerecht so einfach zu verwenden wie asynchron fs.exists und ist ein bisschen schneller als fs.stat .

@GeorgeBailey

Wenn man sich die PR-Notizen ansieht, hat die Lösung von @imyller einige unbeabsichtigte Konsequenzen, außerdem verarbeitet sie nichtexistsSync

Der PR #5098 geht offensichtlich nicht auf existSync ein, da es in dem PR darum geht, Callback-Unterstützung im Nodeback-Stil zu asynchronem exists hinzuzufügen. Diese Art von Änderung gilt in keiner Weise für existSync - und es wäre irreführend, wenn ich in derselben PR Änderungen an existSync vornehmen würde.

Die unbeabsichtigten Folgen, auf die Sie sich beziehen, sind wahr, falls jemand mit Rückrufargumenten verwechselt - das ist wahr. Ich sehe immer noch, dass sich die Situation mit diesem Patch verbessert. Es ist keine perfekte Lösung, aber es mildert die viel schlimmere Situation und macht sie zumindest ein bisschen besser.

@dfabulich , @imyller , ich würde vorschlagen, dass die Funktionen fs. .. Sync Spiegel ihrer asynchronen Verwandten sein sollten. Node.JS wurde nie dafür entwickelt, im Sync-Modus gut zu funktionieren.

In diesem Wissen habe ich vor langer Zeit einen Vorschlag gepostet, weil ich Code im traditionellen Single-Thread-Ansatz schreiben und eine Bibliothek die Übersetzung übernehmen lassen wollte. (Wo normalerweise ein Thread blockiert oder schläft, bedeutet dies Callbacks unter der Haube.) Es wurde vorgeschlagen, stattdessen Knotenfasern zu betrachten.

Ich habe diesen Weg nie vollständig recherchiert, aber wenn wir nach einer praktischen Synchronisierungsfunktion suchen, ist die fs -API möglicherweise nicht der beste Weg, dies zu tun.

@GeorgeBailey Schließlich würde ich gerne ein konsistentes fs.exists und in geringerem Maße fs.existsSync sehen, ich verstehe, dass es nicht über Nacht passieren kann. Andererseits denke ich, dass die Community das gesamte Ökosystem sehr schnell mit neuen Versionen aktualisieren würde, die auf Benutzer abzielen, die neuere Versionen von node. Ich denke, die Frage wäre, wie man den Übergang so schmerzlos wie möglich gestaltet.

Ich habe eine vorhandene Datei, die ich zum Lesen und Anhängen öffne, z.

var fs = require("fs");

fs.open("myFile.txt", "a+", function(fd) {
    // ..
})

Ich möchte jedoch nur zulassen, dass die Datei erfolgreich geöffnet wird, wenn sie bereits vorhanden ist, und andernfalls einen Fehler auslösen.

Ich habe keinen Weg gefunden, dies ohne eine Form der separaten Existenzprüfung zu erreichen. Es gibt kein Flag, das dieser Anforderung entspricht:

(aus der Node-Dokumentation ):

  • 'w' - Datei zum Schreiben öffnen. Die Datei wird erstellt (falls nicht vorhanden) oder abgeschnitten (falls vorhanden).
  • 'wx' - Wie 'w', schlägt aber fehl, wenn der Pfad vorhanden ist.
  • 'w+' - Datei zum Lesen und Schreiben öffnen. Die Datei wird erstellt (falls nicht vorhanden) oder abgeschnitten (falls vorhanden).
  • 'wx+' - Wie 'w+', schlägt jedoch fehl, wenn der Pfad vorhanden ist.
  • 'a' - Datei zum Anhängen öffnen. Die Datei wird erstellt, wenn sie nicht vorhanden ist.
  • 'ax' - Wie 'a', schlägt aber fehl, wenn der Pfad existiert.
  • 'a+' - Datei zum Lesen und Anhängen öffnen. Die Datei wird erstellt, wenn sie nicht vorhanden ist.
  • 'ax+' - Wie 'a+', schlägt jedoch fehl, wenn der Pfad vorhanden ist.

Ein Wechsel zu ax+ anstelle von a+ würde zu einem Fehler open führen, wenn die Datei _existiert_, aber das ist das Gegenteil von dem, was hier benötigt wird. Gibt es eine Möglichkeit, Fehler zu machen, wenn es _nicht_ existiert?

(Meine aktuelle Problemumgehung besteht darin, r+ zu verwenden, aber das bedeutet, dass ich die aktuelle Größe der Datei vor jedem Schreibvorgang überprüfen muss. Dies schafft ein weiteres Problem: Jetzt kann jeder Schreibvorgang für sich selbst anfällig für eine Race-Bedingung sein_)

@rotemdan

const { O_APPEND, O_WRONLY } = require('constants');
fs.open(filename, O_APPEND | O_WRONLY, callback);

@bnoordhuis

Danke. Ich habe diese Flags in der Linux-Manpage bemerkt, aber sie scheinen nicht offiziell in node dokumentiert zu sein.

Frage: Funktioniert das garantiert auch unter Windows? und im Allgemeinen ist es wirklich plattformübergreifend? (Ich vermute, der relevante libuv-Quellcode für Windows ist hier ).

Nicht alle Flags funktionieren unter Windows (zB O_NOATIME), aber die meisten tun es.

Es ist ein undokumentiertes, aber ziemlich stabiles Feature. Die Mode-Strings selbst werden in Form von O_flag-Konstanten implementiert, z. B. ist 'a' eine Abkürzung für O_APPEND | O_CREAT | O_WRONLY .

Wollte gerade ein Thema dazu eröffnen. Die Dokumentation ist unklar, ob der Zugriff ein Ersatz ist. Ich denke, das eigentliche Problem hier ist, wie das nicht Gefundene kommuniziert wird. Wenn access/stat für eine vorhandene Prüfung verwendet werden soll, ist eine Ausnahme bei nicht gefunden zu schwerwiegend. Würde es nicht ausreichen, zurückzugeben, dass es nicht FRWX ist?

fs.exists ist wirklich notwendig. Wenn die API kaputt ist, können wir sie reparieren, anstatt sie zu verwerfen? Können wir andernfalls eine Option für fs.access bereitstellen, wenn der zweite Parameter mode === fs.E_OK dann den Fehler nicht auslöst und den booleschen Wert zurückgibt?

@andyhu Noch einmal: fs.exists tut nicht das, was man nach seinem Namen denken könnte, und das ist ein Problem.

Verwenden Sie einfach fs.access als Ersatz, es macht genau dasselbe für alle fs.exists Anwendungsfälle (außer dass Sie die richtige Callback-Signatur haben).

An dieser Stelle scheint die Frage nach einer API zu sein, die Folgendes tut:

function doesReallyExistSync(path) {
  try { return !fs.accessSync(path, fs.F_OK) } catch (e) { return false }
}

TBH Ich verstehe, dass ich eine so einfache API haben möchte, aber das Problem ist, dass es nicht wirklich so einfach ist. Nehmen Sie das folgende Beispiel, in dem das Verzeichnis b ein symbolischer Link zurück zu a ist:

fs.accessSync('./a' + '/b/a'.repeat(60) + '/c', fs.F_OK)

Was zu Folgendem führen wird:

Error: ELOOP: too many symbolic links encountered, access './a/b/[...]/a/c
    at Error (native)
    at Object.fs.accessSync (fs.js:203:11)

Soll node auch die Logik hinzufügen, nach ELOOP zu suchen und den Pfad durch fs.realpath() laufen zu lassen, um dieses Problem zu umgehen? Wenn dies der Fall ist, würde ich erwarten, dass der Knoten am besten alle plattformspezifischen Probleme in der API kompensiert. Hier wird es etwas haarig, und plötzlich wird eine API auf den ersten Blick eine Zeile komplexer. Genug, meiner Meinung nach, um ein eigenes Modul zu verdienen (es gibt häufig kleinere Module).

Ich habe PR #7455 eingereicht, um die Leistung von existsSync über das hinaus zu verbessern, was in reinem JS möglich ist (indem vermieden wird, dass eine ignorierte Ausnahme ausgelöst wird).

@trevnorris Dein accessSync Beispiel ist ziemlich irre! Glücklicherweise wissen wir bereits, was das Richtige ist: existsSync gibt in diesem Fall false zurück. LGTM, also lehnen wir das einfach ab.

Nur als Referenz habe ich nachgeschlagen, wie andere Sprachen/Laufzeiten dies tun:

Python 3 (verwendet stat , "if stat failed, file is does not exist" -Methode):

# Does a path exist?
# This is false for dangling symbolic links on systems that support them.
def exists(path):
    """Test whether a path exists.  Returns False for broken symbolic links"""
    try:
        os.stat(path)
    except OSError:
        return False
    return True

Java (JDK 8) (verwendet stat , "if stat failed, file is angenommen to exist" -Methode):

JNIEXPORT jint JNICALL
Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
                                                  jobject file)
{
    jint rv = 0;

    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
        int mode;
        if (statMode(path, &mode)) {
            int fmt = mode & S_IFMT;
            rv = (jint) (java_io_FileSystem_BA_EXISTS
                  | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
                  | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
        }
    } END_PLATFORM_STRING(env, path);
    return rv;
}

Perl (verwendet stat , -e (existiert) op ist 1/true wenn stat irgendwelche Ergebnisse zurückgegeben hat, andernfalls false)

my %op = (
    r => sub { cando($_[0], S_IRUSR, 1) },
    w => sub { cando($_[0], S_IWUSR, 1) },
    x => sub { cando($_[0], S_IXUSR, 1) },
    o => sub { $_[0][4] == $>           },

    R => sub { cando($_[0], S_IRUSR, 0) },
    W => sub { cando($_[0], S_IWUSR, 0) },
    X => sub { cando($_[0], S_IXUSR, 0) },
    O => sub { $_[0][4] == $<           },

    e => sub { 1 }     <--- exists

PHP (verwendet stat , "if stat failed, file is does not exist" -Methode):
Ausschnitt aus (filestat.c):

/* {{{ proto bool file_exists(string filename)
   Returns true if filename exists */
FileFunction(PHP_FN(file_exists), FS_EXISTS)

...

    case FS_EXISTS:
        RETURN_TRUE;

Go (verwendet stat mit zusätzlicher Bibliotheksfunktion, um den zurückgegebenen Fehler auf Hinweise auf das Vorhandensein einer Datei zu testen):

if _, err := os.Stat("./thefile.ext"); err != nil {
    if os.IsNotExist(err) {
        // file does not exist
    } else {
        // other error
    }
}

Der Ansatz der Go-Sprache ist am interessantesten: Sie bieten standardmäßige stat und haben dann zwei separate Komfortfunktionen der Kernbibliothek, um festzustellen, ob der zurückgegebene _error_ die Existenz oder Nichtexistenz einer Datei anzeigt os.IsExists(err) und os.IsNotExists(err) .

Mit Ausnahme der Go-Sprache scheint die gebräuchlichste Methode zum Implementieren des Tests "Datei vorhanden" darin zu bestehen, einfach stat auszuführen und davon auszugehen, dass die Datei existiert, wenn etwas / kein Fehler zurückgegeben wird.

@dfabulich

wir wissen bereits, was das Richtige ist: existSync gibt in diesem Fall false zurück.

Nicht genau. fs.realpath() sollte die symbolischen Links zu einem Pfad auflösen, den fs.access() verarbeiten kann (es gibt einen Fehler in v6, wo das nicht funktioniert, an dem ich arbeite). Wenn der Benutzer also ELOOP erhält, kann er die Operation erneut mit fs.access(fs.realpath(path)); aufrufen. Einfach false zurückzugeben wäre technisch falsch.

Für Interessierte:

Ich habe ein Userland-Modul fs-exists-nodeback veröffentlicht

https://github.com/imyller/node-fs-exists-nodeback

Beim Laden füllt das Modul fs.exists mehrfach aus, um sowohl den ursprünglichen Node.js-Callback-Stil als auch den Standardfehler-First-Callback-Stil auf abwärtskompatible Weise zu unterstützen.

Dies kann eine Lösung für diejenigen bieten, die Probleme mit callback(boolean) haben, die nicht mit Bibliotheken funktionieren, die Funktionen mit Nodeback-Standard-Callback erwarten.

Ich denke, https://github.com/nodejs/node/commit/7b5ffa46fe4d2868c1662694da06eb55ec744bde sollte dies beheben / adressieren.

Es hebt existsSync() und behält den inkonsistenten Callback exists() veraltet bei.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen