Mehrere Befehle finden, ausführen und stapeln

1

Ich erstelle ein Skript zum Kopieren / Überprüfen von Dateien ... unter Mac OS X / FreeBSD mit der Möglichkeit, eine Portierung nach CentOS, Debian oder OpenBSD vorzunehmen

Mehr zum Drehbuch:

  1. Überprüfen Sie, ob der Quellpfad Dateien / Unterverzeichnisse enthält
  2. Erstellen Sie eine oder mehrere Prüfsummen für jedes Verzeichnis / Unterverzeichnis
  3. tar / komprimiere in den Zielpfad
  4. Überprüfen Sie die Dateiintegrität im Zielpfad

Natürlich ist es paranoid, weil die Dateiintegritätsprüfungen auf HW / HDD-Ebene durchgeführt werden und von SMART problemlos überprüft werden können, aber zehn Jahre später kann ich die Integrität des Originals nicht überprüfen. Die Prüfsumme wird auf einer CF / XD-Karte erstellt und ist original ... Sie können so viel kopieren, wie Sie möchten, und machen sich keine Gedanken über so genannte faule Bits, HW-Fehler usw.

Natürlich kann auch rsync verwendet werden, aber mir gefällt die Idee, obsolete MD5 / SHA1-Prüfsummen mit möglichen Kollisionen zu verwenden, nicht. Es braucht Stunden Arbeit, "Glück" und Schweiß, um zur richtigen Zeit am richtigen Ort zu sein und ein einzigartiges Foto zu machen ... wenn Sie das Original RAW verlieren ... ist es für immer verschwunden, nur die Erinnerung bleibt.

"Nur Paranoide überleben" - Andy Groove

Ich habe ein einfaches Arbeitsskript für Schritt 1. im Skript:

today=`date +%Y-%m-%d`
CHK='shasum -a512'
CHK_OUTPUT=($today)-checksum.txt
find . -type f ! -name  ".*" -maxdepth 1 -exec $CHK {} \; > "$CHK_OUTPUT"

Ich erhalte die erwartete Prüfsummen-Datei, aber die Frage lautet: "Können wir sie verbessern?"

...cf83e1357eef47417a81a538327af927da3e  ./(2017-07-19)-checksum.txt

Ich will nervig loswerden ./ also habe ich folgendes codiert ...

find ./ -type f ! -name  ".*" -maxdepth 1 -exec bash -c '$CHK $(basename {}) > $CHK_OUTPUT' \;

leider bekomme ich folgenden fehler

bash: ${CHK_OUTPUT}: ambiguous redirect

ein weiterer Versuch

    find ./ -type f ! -name  ".*" -maxdepth 1 -exec bash -c '$CHK $(basename {})' \; > $CHK_OUTPUT

es funktioniert irgendwie aber mit seltsamen ergebnissen

Ich habe mit UTFM & RTFM versagt und ich habe keine Ahnung, wie ich überhaupt Google fragen soll :-D

Kann jemand vorschlagen, wie es geht, bitte?

Grüße

David

David Hajes
quelle
Beachten Sie, dass einige der von Ihnen genannten Betriebssysteme nicht bashstandardmäßig installiert sind.
fd0

Antworten:

2

So übergeben Sie Argumente an eine Subshell in -exec

Mit find‚s -execkönnen Sie komplexe Befehle übergeben Sie einen Sub - Shell verwenden, wie Sie richtig identifiziert. Es gibt jedoch einige Probleme mit Ihrem Ansatz.

Ihre externe Variable $CHKwird nicht erweitert, da sie in einfachen Anführungszeichen steht. Was Sie tun können, um der Subshell mitzuteilen, dass diese Variable bereits vorhanden ist export:

$ foo=bar
$ find . -type f -exec sh -c 'echo "$foo"' \;
(returns an empty line for every file found)

$ export foo=bar
$ find . -type f -exec sh -c 'echo "$foo"' \;
(returns "bar" for every file found)

Durch das Exportieren einer Variablen wird diese Teil Ihrer Umgebung, die von Subshells gelesen werden kann. Oder Sie übergeben es als separates Argument an die Subshell, die hier die Anlaufstelle ist:

$ foo=bar
$ find . -type f -exec sh -c 'echo "$0"' "$foo" \;
(returns "bar" for every file found)

Natürlich können Sie weiter mit $1, $2etc, und verwenden Sie {}für Ihre Subshell als Argument auch den tatsächlichen Dateinamen zu verwenden. Und vergessen Sie nicht, Ihre Variablen anzugeben.

Ihr spezieller Fall

Sie können Ihren Befehl einfach umschreiben in:

shasum -a512 * > "$CHK_OUTPUT"

weil shasumes klug genug ist, die Arbeit in einem Befehl zu erledigen, mehrere Dateien zu lesen, ohne eine Schleife oder find. Enthält standardmäßig *keine Dateien, die mit einem Punkt beginnen (Sie können ihn jedoch mit ändern shopt -s dotglob), sodass Ihre findOptionen nicht erforderlich sind, insbesondere wenn der maxdepthWert 1 ist.

Aber tun wir mal so, shasumals wäre es nicht so schlau, also kann ich Ihnen noch ein paar Optionen geben. Wenn Sie verwenden möchten, gehen findSie folgendermaßen mit mehreren Argumenten um:

CHK='shasum -a512'
find ./ -type f ! -name  ".*" -maxdepth 1 -exec \
sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > "$CHK_OUTPUT"

Aber auch dann kann dies alles besser lesbar umgeschrieben werden:

today=$(date +%Y-%m-%d)
for f in *; do shasum -a512 "$f" > "($today)-checksum.txt"; done

Das Durchlaufen von Dateien ist oft einfacher als das Verwenden find, obwohl es aufgrund der Erweiterung von eine Beschränkung gibt, wie viele Dateien Sie auf diese Weise verarbeiten können *- irgendwann wird es für Ihre Befehlszeile zu lang sein (die spezifische Beschränkung hängt von Ihrer ab) Betriebssystem und Shell).

Und Sie könnten es natürlich rekursiv mit shopt -s globstarBash ≥ 4.0 machen:

shopt -s globstar
for f in **/*; do …; done

Aber das ist wieder dasselbe wie:

shasum -a512 **/* > "$CHK_OUTPUT"
slhck
quelle
danke für die antwort slhck ich habe vergessen zu erwähnen, dass sich der oben genannte befehl bereits in der schleife befindet, die eine einzelne datei liest. Die ganze Idee hinter dem Skript ist "sichere" Kopie von Tausenden von Bildern in vielen Unterverzeichnissen (ich bin Fotograf Alpinist) dann nächstes mal nicht. Möglicherweise gibt es auch Probleme mit Sonderzeichen in Dateinamen wie (2017-07-16) _Hohewand.jpg
David Hajes
@DavidHajes Ich nahm an, dass es Teil eines größeren Skripts sein würde. Wenn Sie spezielle Probleme mit anderen Teilen haben, können Sie gerne eine Frage dazu stellen.
Slhck
Ihre Lösung funktioniert mit nur einem Problem - sie fügt immer wieder unvollendete Prüfsummen in die find ./ -type f ! -name ".*" -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} > "$CHK_OUTPUT" \;
Datei checksum.txt ein
Was meinst du mit "unvollendete" Prüfsumme? Beachten Sie, dass es einen Unterschied zwischen der Verrohrung nach $CHK_OUTPUTvorher oder nachher gibt \;. Wenn Sie dies zuvor getan haben, findwird jedes Mal, wenn eine Datei gefunden wird, zu dieser Datei umgeleitet. Sie sollten dies nach dem findBefehl tun , um die gesamte Ausgabe zu erfassen.
Slhck
Meine erste Version des Skripts hat die Prüfsumme aller Dateien erstellt und die Datei checksum.txt selbst ausgeschlossen ... jetzt ... Die Datei checksum.txt ist in der ersten Zeile enthalten und schlägt offensichtlich bei der nächsten Prüfsumme fehl, da sie geändert wurde, nachdem alle Dateien in einem Ordner mit einer Prüfsumme versehen wurden ..... im
Moment
0

AKTUALISIEREN

Leider erzeugt der unten erwähnte Befehl immer noch eine leere Prüfsumme.txt in Unterverzeichnissen, in denen sich keine Datei (en) befinden.

find ./ -type f \( ! -iname ".*" ! -iname "\(????-??-??\)-checksum.txt" \) -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > $CHK_OUTPUT

Ich weiß nicht warum, aber es gibt Kludge

# remove empty checksum.txt
    if [[ -f $CHK_OUTPUT ]] && [[ ! -s $CHK_OUTPUT ]]; then
        rm $CHK_OUTPUT
    fi

=============================================== ===

endgültige Lösung ... scheint soweit zu funktionieren **

find ./ -type f \( ! -iname ".*" ! -iname "\(????-??-??\)-checksum.txt" \) -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > $CHK_OUTPUT
David Hajes
quelle
Vielen Dank, dass Sie die Lücke in Ihrer Frage geschlossen haben. Dies wäre für andere mit einem ähnlichen Problem viel hilfreicher, wenn Sie die Antwort erweitern könnten, um zu erklären, was dieser Code bewirkt.
Fixer1234
Lies
Ich hätte nicht so vieldeutig sein sollen. :-) Offensichtlich ist das , was es tut, die Frage zu lösen. Was ich wirklich fragte, war, wie und warum es die Frage löst. Können Sie die Parameter der Befehlszeile erklären? Wie der Befehl funktioniert, ist lehrreicher als eine lange Zeile unerklärlichen Codes. "Bringe einem Mann das Fischen bei" und das alles.
Fixer1234
Leider, wenn der Mensch selbst fischen lernt ... ist es schwierig, anderen das Fischen beizubringen :-D
David Hajes