Wie kann ich überprüfen, ob eine Datei in Bash erstellt oder abgeschnitten / überschrieben werden kann?

15

Der Benutzer ruft mein Skript mit einem Dateipfad auf, der an einem bestimmten Punkt im Skript entweder erstellt oder überschrieben wird, z foo.sh file.txt oder foo.sh dir/file.txt.

Das Verhalten beim Erstellen oder Überschreiben entspricht weitgehend den Anforderungen, um die Datei auf der rechten Seite des Ordners zu platzieren > Ausgabe-Umleitungsoperators zu setzen oder sie als Argument an zu übergeben tee(tatsächlich teeist es genau das, was ich tue, wenn ich sie als Argument an übergebe) ).

Bevor ich in den Eingeweiden des Skripts zu bekommen, möchte ich eine angemessene Kontrolle machen , wenn die Datei kann erstellt werden / überschrieben, aber es nicht wirklich schaffen. Diese Prüfung muss nicht perfekt sein, und es ist mir klar, dass sich die Situation zwischen der Prüfung und dem Punkt, an dem die Datei tatsächlich geschrieben wird, ändern kann Lösung besten Aufwand finden , sodass ich frühzeitig aussteigen kann In dem Fall, dass der Dateipfad ungültig ist.

Beispiele für Gründe, aus denen die Datei nicht erstellt werden konnte:

  • Die Datei enthält eine Verzeichniskomponente wie dir/file.txt aber das Verzeichnis direxistiert nicht
  • Der Benutzer hat keine Schreibberechtigung für das angegebene Verzeichnis (oder die CWD, wenn kein Verzeichnis angegeben wurde)

Ja, ich stelle fest, dass das Überprüfen von Berechtigungen "im Voraus" nicht The UNIX Way ™ ist , sondern ich sollte den Vorgang einfach ausprobieren und später um Verzeihung bitten. In meinem speziellen Skript führt dies jedoch zu einer schlechten Benutzererfahrung, und ich kann die verantwortliche Komponente nicht ändern.

BeeOnRope
quelle
Wird das Argument immer auf dem aktuellen Verzeichnis basieren oder kann der Benutzer einen vollständigen Pfad angeben?
Jesse_b
@ Jesse_b - Ich nehme an, der Benutzer könnte einen absoluten Pfad wie angeben /foo/bar/file.txt. Grundsätzlich übergebe ich den Pfad teegerne dahin, tee $OUT_FILEwo OUT_FILEauf der Kommandozeile übergeben wird. Das sollte "nur" mit absoluten und relativen Pfaden funktionieren, oder?
BeeOnRope
@BeeOnRope, nein, du würdest tee -- "$OUT_FILE"zumindest brauchen . Was ist, wenn die Datei bereits existiert oder existiert, aber keine reguläre Datei ist (Verzeichnis, Symlink, FIFO)?
Stéphane Chazelas
@ StéphaneChazelas - naja ich benutze tee "${OUT_FILE}.tmp". Wenn die Datei bereits vorhanden ist, wird teeüberschrieben. Dies ist in diesem Fall das gewünschte Verhalten. Wenn es ein Verzeichnis ist, teewird es fehlschlagen (glaube ich). symlink Ich bin nicht 100% sicher?
BeeOnRope

Antworten:

11

Der naheliegende Test wäre:

if touch /path/to/file; then
    : it can be created
fi

Die Datei wird jedoch tatsächlich erstellt, wenn sie noch nicht vorhanden ist. Wir könnten nach uns selbst aufräumen:

if touch /path/to/file; then
    rm /path/to/file
fi

Dies würde jedoch eine bereits vorhandene Datei entfernen, die Sie wahrscheinlich nicht möchten.

Wir haben jedoch einen Ausweg:

if mkdir /path/to/file; then
    rmdir /path/to/file
fi

Sie können kein Verzeichnis mit demselben Namen wie ein anderes Objekt in diesem Verzeichnis haben. Ich kann mir keine Situation vorstellen, in der Sie ein Verzeichnis erstellen, aber keine Datei erstellen könnten. Nach diesem Test steht es Ihrem Skript frei, ein konventionelles Skript zu erstellen /path/to/fileund damit zu tun, was es will.

DopeGhoti
quelle
2
Das erledigt sehr ordentlich so viele Probleme! Ein Codekommentar, der erklärt und begründet, dass eine ungewöhnliche Lösung die Länge Ihrer Antwort überschreitet. :-)
jpaugh
Ich habe auch if mkdir /var/run/lockdirals Verriegelungstest verwendet. Dies ist atomar, wobei if ! [[ -f /var/run/lockfile ]]; then touch /var/run/lockfilezwischen dem Test und touchdem Start einer anderen Instanz des fraglichen Skripts eine kurze Zeit liegt .
DopeGhoti
8

Nach dem, was ich zusammengetragen habe, möchten Sie dies bei der Verwendung überprüfen

tee -- "$OUT_FILE"

(Beachten Sie, dass das --oder nicht für Dateinamen funktioniert, die mit - beginnen.)tee Die Datei konnte zum Schreiben geöffnet werden.

Das ist das:

  • Die Länge des Dateipfads überschreitet nicht das PATH_MAX-Limit
  • Die Datei existiert (nach der Auflösung von Symlinks) und ist nicht vom Typ Verzeichnis und Sie haben Schreibrechte zu.
  • Wenn die Datei nicht existiert, existiert der Verzeichnisname der Datei (nach der Auflösung von Symlinks) als Verzeichnis und Sie haben Schreib- und Suchberechtigung dafür und die Dateinamenlänge überschreitet nicht das NAME_MAX-Limit des Dateisystems, in dem sich das Verzeichnis befindet.
  • oder die Datei ist ein Symlink, der auf eine Datei verweist, die nicht existiert und keine Symlink-Schleife ist, aber die oben genannten Kriterien erfüllt

Wir werden vorerst Dateisysteme wie vfat, ntfs oder hfsplus ignorieren, die Einschränkungen hinsichtlich der möglichen Bytewerte von Dateinamen, Datenträgerkontingent, Prozesslimit, Selinux, Apparmor oder anderen Sicherheitsmechanismen in der Art, vollständiges Dateisystem, kein Inode mehr, Gerät haben Dateien, die aus irgendeinem Grund nicht auf diese Weise geöffnet werden können, Dateien, die ausführbare Dateien sind, die derzeit in einem Prozessadressraum zugeordnet sind, was sich auch auf die Fähigkeit auswirken kann, die Datei zu öffnen oder zu erstellen.

Mit zsh:

zmodload zsh/system
tee_would_likely_succeed() {
  local file=$1 ERRNO=0 LC_ALL=C
  if [ -d "$file" ]; then
    return 1 # directory
  elif [ -w "$file" ]; then
    return 0 # writable non-directory
  elif [ -e "$file" ]; then
    return 1 # exists, non-writable
  elif [ "$errnos[ERRNO]" != ENOENT ]; then
    return 1 # only ENOENT error can be recovered
  else
    local dir=$file:P:h base=$file:t
    [ -d "$dir" ] &&    # directory
      [ -w "$dir" ] &&  # writable
      [ -x "$dir" ] &&  # and searchable
      (($#base <= $(getconf -- NAME_MAX "$dir")))
    return
  fi
}

bashErsetzen Sie in oder in einer Bourne-ähnlichen Shell einfach die

zmodload zsh/system
tee_would_likely_succeed() {
  <zsh-code>
}

mit:

tee_would_likely_succeed() {
  zsh -s -- "$@" << 'EOF'
  zmodload zsh/system
  <zsh-code>
EOF
}

Die zsh-spezifischen Funktionen sind $ERRNO(die den Fehlercode des letzten Systemaufrufs offenlegen) und das $errnos[]assoziative Array, das in die entsprechenden Standard-C-Makronamen übersetzt werden soll. Und das $var:h(von csh) und $var:P(benötigt zsh 5.3 oder höher).

bash hat noch keine entsprechenden Features.

$file:hkann ersetzt werden dir=$(dirname -- "$file"; echo .); dir=${dir%??}, oder mit GNU dirname: IFS= read -rd '' dir < <(dirname -z -- "$file").

Ein $errnos[ERRNO] == ENOENTAnsatz könnte darin bestehen, ls -Lddie Datei zu verarbeiten und zu prüfen, ob die Fehlermeldung dem ENOENT-Fehler entspricht. Dies zuverlässig und portabel zu tun, ist jedoch schwierig.

Ein Ansatz könnte sein:

msg_for_ENOENT=$(LC_ALL=C ls -d -- '/no such file' 2>&1)
msg_for_ENOENT=${msg_for_ENOENT##*:}

(unter der Annahme, dass die Fehlermeldung mit der Übersetzung von endet und dass diese Übersetzung kein a enthält ) und führen Sie dann stattdessen Folgendes aus:syserror()ENOENT:[ -e "$file" ]

err=$(ls -Ld -- "$file" 2>&1)

Und mit auf einen ENOENT-Fehler prüfen

case $err in
  (*:"$msg_for_ENOENT") ...
esac

Der $file:PTeil ist der schwierigste bash, der unter FreeBSD erreicht werden kann.

FreeBSD verfügt zwar über einen realpathBefehl und einen readlinkBefehl, der eine -fOption akzeptiert , sie können jedoch nicht in den Fällen verwendet werden, in denen es sich bei der Datei um einen Symlink handelt, der nicht aufgelöst werden kann. Das ist das selbe mit perl'sCwd::realpath() .

python's os.path.realpath()scheint ähnlich zu funktionieren zsh $file:P, wenn also mindestens eine Version von pythoninstalliert ist und ein pythonBefehl auf eine von ihnen verweist (was unter FreeBSD nicht gegeben ist), können Sie Folgendes tun:

dir=$(python -c '
import os, sys
print(os.path.realpath(sys.argv[1]) + ".")' "$dir") || return
dir=${dir%.}

Aber dann kannst du das Ganze genauso gut machen python.

Oder Sie könnten entscheiden, nicht alle diese Eckfälle zu behandeln.

Stéphane Chazelas
quelle
Ja, Sie haben meine Absicht richtig verstanden. Tatsächlich war die ursprüngliche Frage unklar: Ich habe ursprünglich über das Erstellen der Datei gesprochen, aber tatsächlich verwende ich sie mit teeder Voraussetzung, dass die Datei tatsächlich erstellt werden kann, wenn sie nicht vorhanden ist, oder auf null gekürzt und überschrieben werden kann wenn ja (oder wie auch immer tee).
BeeOnRope
Anscheinend hat Ihre letzte Bearbeitung die Formatierung
D. Ben Knoble,
Danke, @ bishop, @ D.BenKnoble, siehe edit.
Stéphane Chazelas
1
@DennisWilliamson, na ja, eine Shell ist ein Tool zum Aufrufen von Programmen, und jedes Programm, das Sie in Ihrem Skript aufrufen, muss installiert sein, damit Ihr Skript funktioniert. In macOS sind standardmäßig sowohl bash als auch zsh installiert. FreeBSD kommt mit keinem. Das OP hat vielleicht einen guten Grund, dies in Bash zu tun, vielleicht ist es etwas, das sie zu einem bereits geschriebenen Bash-Skript hinzufügen möchten.
Stéphane Chazelas
1
@pipe wiederum ist eine Shell ein Befehlsinterpreter. Sie werden feststellen , dass viele Shell - Code Auszüge siehe hier geschrieben invoke Interpreten anderer Sprachen wie perl, awk, sedoder python(oder sogar sh, bash...). Beim Shell-Scripting geht es darum, den besten Befehl für die Aufgabe zu verwenden. Hier perlgibt es nicht einmal ein Äquivalent von zsh's $var:P(es Cwd::realpathverhält sich wie BSD realpathoder readlink -fwährend Sie möchten, dass es sich wie GNU readlink -foder python' s verhält os.path.realpath())
Stéphane Chazelas
6

Möglicherweise möchten Sie die Datei zu einem frühen Zeitpunkt erstellen, sie jedoch erst später in Ihrem Skript einfügen. Mit dem execBefehl können Sie die Datei in einem Dateideskriptor (z. B. 3, 4 usw.) öffnen und später eine Umleitung zu einem Dateideskriptor ( >&3usw.) verwenden, um Inhalte in diese Datei zu schreiben.

Etwas wie:

#!/bin/bash

# Open the file for read/write, so it doesn't get
# truncated just yet (to preserve the contents in
# case the initial checks fail.)
exec 3<>dir/file.txt || {
    echo "Error creating dir/file.txt" >&2
    exit 1
}

# Long checks here...
check_ok || {
    echo "Failed checks" >&2
    # cleanup file before bailing out
    rm -f dir/file.txt
    exit 1
}

# We're ready to write, first truncate the file.
# Use "truncate(1)" from coreutils, and pass it
# /dev/fd/3 so the file doesn't need to be reopened.
truncate -s 0 /dev/fd/3

# Now populate the file, use a redirection to write
# to the previously opened file descriptor.
populate_contents >&3

Sie können auch a verwenden trap, um die Datei bei einem Fehler zu bereinigen. Dies ist eine gängige Praxis.

Auf diese Weise erhalten Sie eine echte Prüfung auf Berechtigungen, mit denen Sie die Datei erstellen können, während Sie sie gleichzeitig so früh ausführen können, dass Sie nicht auf die langen Prüfungen gewartet haben, wenn dies fehlschlägt.


AKTUALISIERT: Um zu vermeiden, dass die Datei bei fehlgeschlagenen Überprüfungen überladen wird, verwenden Sie die bash- fd<>fileUmleitung, die die Datei nicht sofort abschneidet. (Es ist uns nicht wichtig, aus der Datei zu lesen. Dies ist nur eine Problemumgehung, sodass wir sie nicht abschneiden. Das Anhängen mit >>würde wahrscheinlich auch funktionieren. Ich finde diese Datei jedoch etwas eleganter und halte das O_APPEND-Flag aus des Bildes.)

Wenn wir bereit sind, den Inhalt zu ersetzen, müssen wir die Datei zuerst abschneiden (andernfalls bleiben die nachfolgenden Bytes erhalten, wenn wir weniger Bytes schreiben, als zuvor in der Datei enthalten waren. ) Befehl von coreutils für diesen Zweck, und wir können ihm den geöffneten Dateideskriptor übergeben, den wir haben (unter Verwendung der /dev/fd/3Pseudodatei), damit er die Datei nicht erneut öffnen muss. (Auch hier würde technisch gesehen : >dir/file.txtwahrscheinlich etwas Einfacheres funktionieren, aber die Datei nicht erneut öffnen zu müssen, ist eine elegantere Lösung.)

filbranden
quelle
Ich hatte dies in Betracht gezogen, aber das Problem ist, dass der Benutzer wahrscheinlich verärgert darüber ist, dass die angegebene Datei überschrieben wurde, wenn irgendwo zwischen dem Erstellen der Datei und dem späteren Zeitpunkt ein anderer unschuldiger Fehler auftritt .
BeeOnRope
1
@BeeOnRope Eine weitere Option, mit der die Datei nicht überladen wird, ist der Versuch, nichts daran anzuhängen : echo -n >>fileoder true >> file. Natürlich hat ext4 nur anhängbare Dateien, aber Sie könnten mit diesem falschen Positiv leben.
Mosvy
1
@BeeOnRope Wenn Sie entweder (a) die vorhandene Datei oder (b) die (vollständige) neue Datei beibehalten müssen, ist dies eine andere Frage als die von Ihnen gestellte. Eine gute Antwort darauf ist, die vorhandene Datei my-file.txt.backupvor dem Erstellen eines neuen Namens unter einen neuen Namen zu verschieben (z. B. ). Eine andere Lösung besteht darin, die neue Datei in eine temporäre Datei im selben Ordner zu schreiben und sie dann über die alte Datei zu kopieren, nachdem der Rest des Skripts erfolgreich war. Wenn dieser letzte Vorgang fehlgeschlagen ist, kann der Benutzer das Problem manuell beheben, ohne zu verlieren sein oder ihr Fortschritt.
Jpaugh
1
@BeeOnRope Wenn Sie sich für die "atomic replace" -Route entscheiden (die eine gute ist!), Sollten Sie die temporäre Datei normalerweise in dasselbe Verzeichnis wie die endgültige Datei schreiben (sie müssen sich zum Umbenennen im selben Dateisystem befinden) (2) um erfolgreich zu sein) und einen eindeutigen Namen zu verwenden (wenn also zwei Instanzen des Skripts ausgeführt werden, werden die temporären Dateien des anderen nicht blockiert.) Das Öffnen einer temporären Datei zum Schreiben in dasselbe Verzeichnis ist normalerweise ein guter Hinweis kann es später umbenennen (naja, außer wenn das endgültige Ziel existiert und ein Verzeichnis ist), so dass es in gewissem Umfang auch Ihre Frage beantwortet.
Filbranden
1
@jpaugh - das mache ich nur ungern. Es würde unweigerlich zu Antworten führen, die versuchen, mein übergeordnetes Problem zu lösen, was möglicherweise Lösungen zulässt, die nichts mit der Frage "Wie kann ich überprüfen ..." zu tun haben. Unabhängig davon, was mein Hauptproblem ist, denke ich, dass diese Frage für sich genommen etwas ist, was Sie vernünftigerweise tun möchten. Es wäre unfair für Leute, die diese spezielle Frage beantworten, wenn ich sie auf "Lösen dieses speziellen Problems, das ich mit meinem Skript habe" ändere. Ich habe diese Details hier eingefügt, weil ich gefragt wurde, aber ich möchte die Hauptfrage nicht ändern.
BeeOnRope
4

Ich denke, die Lösung von DopeGhoti ist besser, aber das sollte auch funktionieren:

file=$1
if [[ "${file:0:1}" == '/' ]]; then
    dir=${file%/*}
elif [[ "$file" =~ .*/.* ]]; then
    dir="$(PWD)/${file%/*}"
else
    dir=$(PWD)
fi

if [[ -w "$dir" ]]; then
    echo "writable"
    #do stuff with writable file
else
    echo "not writable"
    #do stuff without writable file
fi

Das erste if-Konstrukt prüft, ob das Argument ein vollständiger Pfad ist (beginnt mit /) und setzt die dirVariable bis zum letzten auf den Verzeichnispfad /. Wenn das Argument nicht mit einem beginnt, /sondern ein enthält /(wobei ein Unterverzeichnis angegeben wird), wird es dirauf das aktuelle Arbeitsverzeichnis + den Pfad des Unterverzeichnisses gesetzt. Andernfalls wird das aktuelle Arbeitsverzeichnis angenommen. Anschließend wird geprüft, ob dieses Verzeichnis beschreibbar ist.

Jesse_b
quelle
4

Was ist mit der Verwendung eines normalen testBefehls wie unten beschrieben?

FILE=$1

DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'

if [ -d $DIR ] ; then
  echo "base directory $DIR for file exists"
  if [ -e $FILE ] ; then
    if [ -w $FILE ] ; then
      echo "file exists, is writeable"
    else
      echo "file exists, NOT writeable"
    fi
  elif [ -w $DIR ] ; then
    echo "directory is writeable"
  else
    echo "directory is NOT writeable"
  fi
else
  echo "can NOT create file in non-existent directory $DIR "
fi
Jaleks
quelle
Ich habe diese Antwort fast dupliziert! Gute Einführung, aber Sie könnten es erwähnen man test, und das [ ]entspricht einem Test (nicht mehr allgemein bekannt). Hier ist ein Einzeiler, den ich in meiner minderwertigen Antwort verwenden wollte, um den genauen Fall für die Nachwelt abzudecken:if [ -w $1] && [ ! -d $1 ] ; then echo "do stuff"; fi
Iron Gremlin
4

Sie erwähnten, dass die Benutzererfahrung Ihre Frage vorantreibt. Ich werde aus einem UX-Blickwinkel antworten, da Sie auf technischer Seite gute Antworten haben.

Wie wäre es, die Ergebnisse in eine temporäre Datei zu schreiben und sie dann ganz am Ende in die vom Benutzer gewünschte Datei zu legen, anstatt die Prüfung im Voraus durchzuführen ? Mögen:

userfile=${1:?Where would you like the file written?}
tmpfile=$(mktemp)

# ... all the complicated stuff, writing into "${tmpfile}"

# fill user's file, keeping existing permissions or creating anew
# while respecting umask
cat "${tmpfile}" > "${userfile}"
if [ 0 -eq $? ]; then
    rm "${tmpfile}"
else
    echo "Couldn't write results into ${userfile}." >&2
    echo "Results available in ${tmpfile}." >&2
    exit 1
fi

Das Gute an diesem Ansatz: Es erzeugt die gewünschte Operation im normalen Happy-Path-Szenario, umgeht das Atomicity-Problem von Test und Set, behält die Berechtigungen der Zieldatei bei, während es bei Bedarf erstellt wird, und ist kinderleicht zu implementieren.

Hinweis: Hätten wir verwendet mv, würden wir die Berechtigungen der temporären Datei behalten - wir wollen das nicht, denke ich: wir wollen die Berechtigungen behalten, die für die Zieldatei festgelegt wurden.

Das Schlimme: Es benötigt das Doppelte des Speicherplatzes ( cat .. >Konstrukt), zwingt den Benutzer zu manueller Arbeit, wenn die Zieldatei zum erforderlichen Zeitpunkt nicht beschreibbar war, und lässt die temporäre Datei herumliegen (was möglicherweise Sicherheit bietet oder Wartungsprobleme).

Bischof
quelle
Tatsächlich mache ich das mehr oder weniger jetzt. Ich schreibe die meisten Ergebnisse in eine temporäre Datei und führe dann am Ende den letzten Verarbeitungsschritt aus und schreibe die Ergebnisse in die endgültige Datei. Das Problem ist, dass ich früh (zu Beginn des Skripts) aussteigen möchte, wenn dieser letzte Schritt wahrscheinlich fehlschlägt. Das Skript wird möglicherweise einige Minuten oder Stunden lang unbeaufsichtigt ausgeführt. Sie möchten also unbedingt wissen, dass es zum Scheitern verurteilt ist!
BeeOnRope
Sicher, aber es gibt so viele Möglichkeiten, wie dies fehlschlagen könnte: Die Festplatte könnte sich füllen, das Upstream-Verzeichnis könnte entfernt werden, die Berechtigungen könnten sich ändern, die Zieldatei könnte für andere wichtige Dinge verwendet werden, und der Benutzer hat vergessen, dass er dieselbe Datei zugewiesen hat, um sie dadurch zu zerstören Betrieb. Wenn wir dies aus einer reinen UX-Perspektive betrachten, ist es vielleicht das Richtige, es wie eine Auftragsübertragung zu behandeln: Wenn Sie am Ende wissen, dass es ordnungsgemäß funktioniert hat, teilen Sie dem Benutzer einfach mit, wo sich der resultierende Inhalt befindet und bieten Sie ihn an ein vorgeschlagener Befehl für sie, es selbst zu bewegen.
Bischof
Theoretisch gibt es unendlich viele Möglichkeiten, wie dies scheitern könnte. In der Praxis ist die überwiegende Mehrheit der Fehlschläge darauf zurückzuführen, dass der angegebene Pfad nicht gültig ist. Ich kann nicht vernünftigerweise verhindern, dass ein betrügerischer Benutzer den FS gleichzeitig ändert, um den Job während des Vorgangs abzubrechen, aber ich kann mit Sicherheit die Fehlerursache Nr. 1 für einen ungültigen oder nicht beschreibbaren Pfad überprüfen.
BeeOnRope
2

TL; DR:

: >> "${userfile}"

Im Gegensatz zu <> "${userfile}"oder touch "${userfile}"werden hierdurch keine falschen Änderungen an den Zeitstempeln der Datei vorgenommen und es wird auch mit Nur-Schreib-Dateien gearbeitet.


Aus dem OP:

Ich möchte eine vernünftige Prüfung durchführen, ob die Datei erstellt / überschrieben werden kann, aber nicht tatsächlich erstellt.

Und von Ihrem Kommentar zu meiner Antwort aus einer UX-Perspektive :

Die überwiegende Mehrheit der Fehler ist darauf zurückzuführen, dass der angegebene Pfad nicht gültig ist. Ich kann nicht vernünftigerweise verhindern, dass ein betrügerischer Benutzer gleichzeitig den FS ändert, um den Job während des Vorgangs abzubrechen, aber ich kann mit Sicherheit die Fehlerursache Nr. 1 für einen ungültigen oder nicht beschreibbaren Pfad überprüfen.

Der einzige zuverlässige Test open(2)bezieht sich auf die Datei, da nur dadurch alle Fragen zur Schreibbarkeit gelöst werden: Pfad, Eigentümer, Dateisystem, Netzwerk, Sicherheitskontext usw. Bei jedem anderen Test wird ein Teil der Schreibbarkeit behandelt, andere jedoch nicht. Wenn Sie eine Untergruppe von Tests wünschen, müssen Sie letztendlich auswählen, was für Sie wichtig ist.

Aber hier ist ein anderer Gedanke. Von dem, was ich verstehe:

  1. Der Prozess zur Erstellung von Inhalten ist langwierig
  2. Die Zieldatei sollte in einem konsistenten Zustand belassen werden.

Sie möchten diese Vorabprüfung aufgrund von Nummer 1 durchführen und möchten aufgrund von Nummer 2 nicht mit einer vorhandenen Datei fummeln. Warum fordern Sie die Shell nicht einfach auf, die Datei zum Anhängen zu öffnen, sondern hängen tatsächlich nichts an?

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

$ for p in file_r dir_r/foo file_w dir_w/foo; do : >> $p; done
-bash: file_r: Permission denied
-bash: dir_r/foo: Permission denied

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
   └── [-rw-rw-r--           0]  foo
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

Unter der Haube löst dies die Beschreibbarkeitsfrage genau wie gewünscht:

open("dir_w/foo", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3

aber ohne den Inhalt oder die Metadaten der Datei zu ändern. Nun ja, dieser Ansatz:

  • Sie erfahren nicht, ob die Datei nur angehängt wird. Dies ist möglicherweise ein Problem, wenn Sie sie am Ende der Inhaltserstellung aktualisieren. Sie können dies bis zu einem gewissen Grad feststellen lsattrund entsprechend reagieren.
  • Erstellt eine Datei, die vorher nicht existiert hat, falls dies der Fall ist: Reduzieren Sie dies mit einer Auswahl rm.

Während ich (in meiner anderen Antwort) behaupte, dass der benutzerfreundlichste Ansatz darin besteht, eine temporäre Datei zu erstellen, die der Benutzer verschieben muss, denke ich, dass dies der am wenigsten benutzerfeindliche Ansatz ist, um seine Eingaben vollständig zu überprüfen.

Bischof
quelle