finden | xargs shasum erstellt die Prüfsumme der Prüfsummendatei selbst (vorzeitig) und schlägt beim Überprüfen fehl

10

Mein Problem (in einem Skript mit #!/bin/sh) ist wie folgt: Ich versuche, alle Dateien in einem Verzeichnis zu Archivierungszwecken zu überprüfen. Die Prüfsummendatei (in meinem Fall sha1) mit allen Dateinamen sollte sich im selben Verzeichnis befinden. Nehmen wir an, wir haben ein Verzeichnis ~/testmit Dateien f1und f2:.

mkdir ~/test
cd ~/test
echo "hello" > f1
echo "world" > f2

Berechnen Sie nun die Prüfsummen mit

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum

macht genau das, was ich will, listet nur alle Dateien des aktuellen Verzeichnisses auf und berechnet die sha1-Summen (maxdepth kann später geändert werden). Die Ausgabe auf STDOUT ist:

f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2

Leider beim Versuch, dies in einer Datei mit zu speichern

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum > sums.sha1

Die resultierende Datei zeigt die Prüfsumme für sich selbst an:

da39a3ee5e6b4b0d3255bfef95601890afd80709  sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2  

und schlägt daher zu einem späteren shasum --checkZeitpunkt aufgrund des offensichtlichen Problems der zusätzlichen Dateimodifikation beim Speichern der letzten Summe fehl .

Ich habe mich umgesehen und mithilfe von -pflag for xargsherausgefunden, dass die Ausgabedatei irgendwie erstellt wird, bevor der Befehl find ausgeführt wird. Daher wird die zusätzliche Datei gefunden und mit einer Prüfsumme versehen ...

Ich weiß, dass ich als Problemumgehung die Prüfsumme an einem anderen Ort speichern könnte (temporäres Verzeichnis über mktemp) oder sie in find spezifisch ausschließen könnte, aber ich würde gerne verstehen, warum sie sich so verhält, wie sie sich verhält - was in meinen Augen nicht so nützlich ist. Wenn der erste Befehl beispielsweise prüfen würde, ob sich die Ausgabedatei bereits auf der Festplatte befindet, würde er niemals die richtige Antwort erhalten ...

user121391
quelle
8
Es ist nicht so xargs, dass die Shell selbst diese Datei erstellt, da die Shell vor der Ausführung eines Befehls zunächst alle Eingaben, Ausgaben und Pipes umleitet, sodass finddie Ausgabedatei beim Start bereits vorhanden ist. Verwenden Sie -execstattdessen:find -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
jimmij
@jimmij, das funktioniert auch nicht garantiert, wenn mehrere Aufrufe sherforderlich sind. Beachten Sie, dass Sie ein Argument benötigen für $0vor {}.
Stéphane Chazelas
@jimmij Deine andere Antwort, die vorgeschlagen wurde, teeist verschwunden? Ich habe es versucht und es funktioniert gut, ich habe auch STDOUT mit dem Hinzufügen von unterdrückt 1>/dev/null. War etwas mit der Antwort nicht in Ordnung oder war es ein Fehler?
user121391
@ user121391 Stephane wies darauf hin, dass es manchmal Probleme mit den Rennbedingungen geben kann, was wahr zu sein scheint. Ich habe es für eine Weile gelöscht, damit Sie es sehen können, aber wenn Sie viele Dateien auf der Liste haben, könnte dieser Befehl schief gehen.
Jimmy
@ Jimmyij ah, ich verstehe. Es könnte hilfreich sein, wenn Sie ihm eine Warnung zu den Problemen voranstellen, da es meiner Meinung nach nicht so bekannt ist, dass dies passieren kann. Andernfalls hätte ich Ihre Antwort für die Fälle akzeptiert, wenn wiederkehrende Läufe die alte Datei und Anthons für Fälle enthalten würden, in denen sie überschrieben werden sollte.
user121391

Antworten:

12

Sie können verhindern, dass die Datei erreicht wird, xargsindem Sie:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\n' |
  xargs -r shasum -- > sums.sha1

Um Probleme mit Dateinamen zu vermeiden, die Leerzeichen oder Zeilenumbrüche oder Anführungszeichen oder Backslashes enthalten, würde ich jedoch Folgendes verwenden:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\0' |
  xargs -r0 shasum -- > sums.sha1

stattdessen.

Dies --dient dazu, Probleme mit Dateinamen zu vermeiden, die mit beginnen -. Es hilft jedoch nicht für eine aufgerufene Datei -. Hätten Sie -print0stattdessen verwendet -printf '%P\0', hätten Sie das nicht benötigt --und hätten kein Problem mit der -Datei gehabt .

Anthon
quelle
Ihre Lösung habe ich letztendlich verwendet. Mir gefällt besonders, dass nachfolgende Läufe die Prüfsummendatei nicht erneut aufbereiten und das Verzeichnis aufblasen. Außerdem habe ich in meinem Skript basenameden Dateinamen sums.sha1 aus dem angegebenen vollständigen Pfad abgerufen (dies war nicht in der Frage enthalten, könnte aber anderen helfen).
user121391
7

Da Sie verwenden -maxdepth 1, gehe ich davon aus, dass Sie keine Rekursion möchten. Wenn ja, machen Sie es stattdessen einfach in der Shell:

for f in ~/test/*; do
    shasum -- "$f"
done > sums.sha1

So überspringen Sie Verzeichnisse:

for f in ~/test/*; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Wenn Sie eine Rekursion benötigen und verwenden bash, gehen Sie wie folgt vor :

shopt -s globstar
for f in ~/test/**; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Beachten Sie, dass alle diese Ansätze den Vorteil haben, an beliebigen Dateinamen zu arbeiten, einschließlich solcher mit Leerzeichen, Zeilenumbrüchen oder anderen Elementen.

terdon
quelle
Ich denke, Sie würden erwähnen, dass dies alle Probleme löst, die das OP mit Dateinamen mit Zeilenumbrüchen haben würde. Wenn das sums.sha1bereits vorhanden ist (aus einem früheren Lauf), wird es von Ihrer Lösung integriert.
Anthon
Entschuldigung, ich habe vorher nicht geklärt: Die maximale Tiefe wurde nur in diesem Beispiel verwendet. Ich verwende eine Funktion, bei der der Benutzer / das Skript beliebige Werte liefern kann, obwohl ich derzeit nur Tiefe 1 benötige.
user121391
@ user121391 siehe aktualisierte Antwort für einen rekursiven Ansatz.
Terdon
Beachten Sie, dass auch versucht wird, andere Arten von nicht regulären Dateien wie Pipes, Geräte ... (und Symlinks zu diesen) zu überprüfen.
Stéphane Chazelas
Vielen Dank, ich persönlich benutze sh, aber Ihre Antwort könnte anderen helfen.
user121391
4

mit zsh:

shasum -- *(D.) > sums.sha1

Der Glob wird vor der Umleitung erweitert, sodass der Glob sums.sha1nicht berücksichtigt wird, wenn er überhaupt nicht vorhanden war.

Dist, Punkt-Dateien (versteckte Dateien) wie findzu enthalten. .ist, nur reguläre Dateien (wie Ihre -type f) auszuwählen .

Um das sums.sha1trotzdem auszuschließen, falls es überhaupt da war:

setopt extendedglob # best in ~/.zshrc
shasum -- ^sums.sha1(D.) > sums.sha1

Beachten Sie, dass diese einen Shasum-Befehl ausführen, sodass bei einer großen Liste möglicherweise der Fehler "Arg-Liste zu lang" angezeigt wird. Um das zu umgehen:

autoload zargs
zargs -e/ -- *(D.) / shasum > sums.sha1

Ich würde empfehlen, ./*anstatt *zu verwenden, um mögliche Probleme mit einer aufgerufenen Datei zu vermeiden -.

Stéphane Chazelas
quelle
Ich habe die Frage mit der Art der Shell bearbeitet, aber Ihre Antwort erinnert mich daran, dass ich vor einiger Zeit zu zsh wechseln wollte ...;)
user121391
1

Wie in den anderen Antworten bereits erwähnt, besteht das Problem darin, dass die Shell die sums.sha1Datei öffnet und erstellt , bevor Sie Ihre Pipeline ausführen. Sie können das Programm verwenden, spongedas Teil des moreutilsPakets vieler Distributionen ist. Im Gegensatz zur Shell spongewird die Umleitung warten, bis alles empfangen wurde, bevor die Datei geöffnet wird. Es wird im Allgemeinen verwendet, wenn Sie eine Datei schreiben möchten, die Sie in derselben Pipeline gelesen haben.

In Ihrem Fall wird es folgendermaßen verwendet:

$ find -maxdepth 1 -type f -printf '%P\n' |xargs shasum |sponge sums.sha1
$ cat sums.sha1
31836aeaab22dc49555a97edb4c753881432e01d  B
7d157d7c000ae27db146575c08ce30df893d3a64  A
TimWolla
quelle
0

Als Alternative zu find / xargs usw. möchten Sie möglicherweise sha1deep. Es ist wahrscheinlich in einem anderen Paket - auf meiner Box kommt es im md5deep-Paket.

Wie andere gesagt haben, wird sums.sha1 von der Shell erstellt, noch bevor find gefunden wird. Ein Trick mit ! -name sums.sha1zu findwird funktionieren, wie wird

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
Torinthiel
quelle