Bash Testen, ob zwei Files genau gleich sind, in POSIX-Shell

Was ist denn der eigentliche Anwendungsfall? Wenn beide Dateien lokal vorliegen kannst du auch stattdessen einen hardllink erstellen, das geht mit Abstand am schnellsten und da die Dateien dann auf den selben Speicherbereich verweisen, sind die zwangsläufig identisch.

Hashes verwendet man ja, weil man normalerweise erst daran interessiert ist, ob zwei Dateien identisch sind, wenn man sie nach irgendwo 'extern' verschoben hat.
 
Der Anwendungsfall ist (vereinfacht gesagt) einerseits die Protokollierung, aber auch sicherzustellen, ob sich eine Web-Quelle (also Ressource) inzwischen geändert hat oder nicht, und nur dann das eine File durch das neue File zu ersetzen.
 
Wenn du die Datei aus dem Internet lädst, dann solltest/kannst du sie schon während des Ladevorgangs (in dem RAM, nicht auf die Festplatte) vergleichen, jedenfalls wenn es dir mehr um "schnelleren" statt "einfacheren" Weg geht (das sind natürlich zwei Ziele, die miteinander in Konflikt stehen).

Da du "sollte POSIX konform sein" muss es vermutlich nicht POSIX konform sein. Wüssste dann nicht, wieso man das überhaupt machen sollte, aber ohne konkrete Infos zum Anwendungsfall kann man das natürlich nicht abschließend beurteilen.
 
  • Gefällt mir
Reaktionen: madmax2010
Die Dateien immer wieder auf Verdacht runterladen ist echt frech da es dem Webseiten-Betreiber unnötige Kosten verursacht.

curl hat dafür die -z Option, bei der man die existierende Datei angeben kann, dann schickt es in der Anfrage einen If-Modified-Since header mit. Wenn der Server weiß dass die Datei seit diesem Datum nicht modifiziert wurde kriegt man nur einen HTTP 304 zurück mit 0 bytes body. Auch die Option -R bietet sich an damit die mtime der lokalen Datei auf den Last-Modified Header vom Server gesetzt wird.

wget kann den Vergleich auch mit der Option -N. wget ist allerdings recht nervig an der Stelle weil es die neue Version immer in eine neue Datei <name>.# schreibt, wobei # hochzählt und man es nicht zwingend kann die bisherige Datei zu überschreiben wenn man -N benutzt. Ist aber vielleicht auch das was du willst.

Programmiert man das selber kann man auch erstmal einen HEAD request an den Server stellen wo man nur die Header zurück kriegt und Content-Length, Last-Modified und ggf. ETag selber vergleichen.
 
  • Gefällt mir
Reaktionen: BeBur und madmax2010
Marco01_809 schrieb:
Die Dateien immer wieder auf Verdacht runterladen ist echt frech
Woher nimmst du die etwas dreiste Annahme, ich würde etwas auf Verdacht tun? Es kann durchaus sein, dass der menschliche Faktor auch eine Rolle spielt und ein Fehler gemacht wurde. - Oder das Quelle und Senke nicht im gleichen Rechenzentrum wären? So brauchen wir wohl nicht weiterdiskutieren 🤷‍♂️

Aber hey, Hauptsache Likes bekommen
 
ich habe like geklickt weil er die Erklärung und Anleitung zu meinem 2-Zeiler getippt hat. Dieser wiederum eine Antwort auf @BeBur zurückfragen zum Usecase und die Antworten drauf gedacht war. Um aus einem anderen Winkel aufs Problem zu schauen. Daher
Niemand will dir was böses. Im internet hat niemand deinen Lebenslauf und deine Fähigkeiten gesehen
Und je nachdem wo @Marco01_809 in welcher position arbeitet, istdas vielleicht ein thema welches ihm 3x a Tag auf die nerven geht

die ?RWTH? Wollte mal den media.ccc.de mirror von halifax werfen, weil eine einzelne datei dort mit relativ regelmäßige recht viel traffic verursachte und sie den verursacher nicht geblockt bekommen haben.
War am Ende des Tages jemand mit zu vielen offenen Tags, bei dem der Browser immer wieder retried hat.

Ich habe neulich auch in einem projekt versehentlich 150TB 2x über den Atlantik, statt ans andere Ende der Stadt geschickt. Kein Unterstellen notwendig. Einfach verkackt :D
Rechnung kam,
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: kali-hi
Ja ok, das ist nachvollziehbar...

Btw., die Browser lassen gerne mal die TCP-Verbindungen usw. im Hintergrund geöffnet... Das ist etwas, was mich an den nicht Firefox-Browsern stört. Aber auf mich hört eh keiner.

... Das hat aber doch nix mehr direkt mit diesem Thema zu tun, hier war eigentlich schon gelöst (auch, wenn ich auf die Flanke von Marco reagiert hatte)
 
  • Gefällt mir
Reaktionen: madmax2010
kali-hi schrieb:
Woher nimmst du die etwas dreiste Annahme, ich würde etwas auf Verdacht tun?
Hattest du das nicht selber gesagt?
kali-hi schrieb:
Der Anwendungsfall ist (vereinfacht gesagt) [...] sicherzustellen, ob sich eine Web-Quelle (also Ressource) inzwischen geändert hat oder nicht, [...]
In deiner Ursprungsfrage geht es darum zwei lokale Dateien zu vergleichen.
=> Daraus lässt sich nur schlussfolgern, dass du eine Datei wiederholt von einer Webseite herunterlädst, BEVOR du weißt ob sie sich geändert hat.
Damit ist die Definition meines Erachtens erfüllt.
auf Verdacht (umgangssprachlich: ohne es genau zu wissen; in der Annahme, dass es richtig, sinnvoll o. ä. ist: auf Verdacht etwas besorgen, sagen)
Bei einer Dateigröße von 1GB ist der erzeugte Traffic nicht unerheblich, je nachdem wie oft das Script läuft.

Natürlich kann es berechtigte Gründe geben, dies so zu tun:
  • Zur Forensik; man will vom Betreiber nicht so leicht erkannt werden, oder man vermutet dass der Server bedingte Anfragen irrtümlich oder böswillig falsch beantwortet
  • Der Server beherrscht weder If-Modified-Since, noch Last-Modified, noch ETag/If-None-Match. In dem Fall ist der Server einfach schäbig und der Betreiber hat selber Schuld wenn er unnötigen Traffic bekommt
Das ist aber sicher nicht der Regelfall. Ferner ist zu beachten, dass die Vorgehensweise für dich selbst ja auch nachteilig ist; unnötiger Traffic, zusätzlicher Speicherverbrauch, erheblich langsamer...
Da es eine Alternative gibt die nicht nur allen Beteiligten Zeit und Geld spart, sondern auch noch einfacher ist (nur eine Option setzen statt ein ganzes Script schreiben!) habe ich meine Freizeit hier gerne investiert um:
  • Die manpages von curl und wget zu durchsuchen, entsprechende Abschnitte zu lesen
  • Den MDN Artikel zum If-Modified-Since header überfliegen zum Gegencheck
  • Einen Post zu schreiben der die Optionen nicht nur benennt sondern auch ihre Funktionsweise zusammenfasst, dann nochmal für zweites Programm und dann noch einen 3. Lösungsweg umrissen
Nichts zu danken, habe ich gern gemacht 🥰
 
  • Gefällt mir
Reaktionen: trb85, madmax2010 und dms
Marco01_809 schrieb:
Hattest du das nicht selber gesagt?
Du hast "sicherstellen" zu streng interpretiert. Genauer gesagt, bekomme ich eine Mitteilung per Messenger vom anderen Server Menschen, dass sich eine Ressource geändert hat. Weil Menschen aber Fehler machen können, kann es sein, dass sich tatsächlich noch nichts geändert hat. Für diesen seltenen und unwahrscheinlichen Fall muss ich die Inhalte vergleichen können, um sicherzustellen, dass es tatsächlich eine Änderung gab.

=> Man kann auch etwas sicherstellen, was in den meisten Fällen "nicht nötig" ist... Vergleiche es vielleicht mit einer Checkliste im Flugzeug... Wie oft sind alle Checks grün?

So schwer zu verstehen ist das doch nicht. Und es geht um 1 GB, nicht 150 TB... und die Daten gehen von D nach D...

Marco01_809 schrieb:
habe ich meine Freizeit hier gerne investiert um:
Dafür bin ich ja auch dankbar, aber ich habe gleichzeitig auch geschrieben, dass ich erst checken muss, ob der andere Server modified oder etag unterstützt.

***

So... curl -I (-I, --head Show document info only) sagt:

Code:
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 18 Oct 2025 07:47:29 GMT
Content-Type: application/zip
Content-Length: 123...
Connection: keep-alive
X-Powered-By: Express
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Thu, 16 Oct...
ETag: ...

(sensible Infos durch ... ersetzt)

Damit wäre die Sache doch klar?
 
  • Gefällt mir
Reaktionen: Marco01_809
kali-hi schrieb:
Der Anwendungsfall ist (vereinfacht gesagt) einerseits die Protokollierung, aber auch sicherzustellen, ob sich eine Web-Quelle (also Ressource) inzwischen geändert hat oder nicht, und nur dann das eine File durch das neue File zu ersetzen.
Code:
curl https://foo.org/bar | cmp /tmp/bar -
Und die Exitcodes nach diesem Muster auswerten: https://www.cyberciti.biz/faq/unix-linux-bash-find-out-the-exit-codes-of-all-piped-commands/

Und wenn cmp(1) den ersten Unterschied findet und abbricht bekommt der curl(1) ein "broken pipe" und beendet sich. D.h.: Bei Unterschieden weit vorne wird auch nicht alles runtergeladen. Falls du den Kram der runtergeladen wurde grundsätzlich brauchst baue ein tee(1) in die Pipe ein.

Zur Verdeutlichung:
Code:
$ echo Hallo > /tmp/Kackwurst
$ echo Hallo | cmp /tmp/Kackwurst -
$ echo Hall | cmp /tmp/Kackwurst -
/tmp/Kackwurst - differ: byte 5, line 1
$
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: madmax2010 und BeBur
Mit Verlaub .. das (nun eröffnete) Problem ist mutmasslich mit RSYNC besser zu lösen -

@kali-hi da du ja den Servermenschen zu kennen scheinst
 
  • Gefällt mir
Reaktionen: madmax2010
Es gibt dabei leider ein Problem:

Bash:
#!/bin/bash
set -e

d1=$(date -r old_file_1)
echo "$d1"
d1=$(date -d "$d1" +%s)
echo "$d1"
export TZ=GMT
d2=$(curl -sI https://new_file_1 | grep Last-Modified)
d2=${d2:15}
echo "$d2"
d2=$(date -d "$d2" +%s)
echo "$d2"
date -d @"$d1"
date -d @"$d2"
if [ "$d1" -ge "$d2" ]; then
    echo "Nothing to do."
    exit 0
fi

old_file_1 scheint 2,5 Stunden älter zu sein als new_file_1, obwohl es keine Änderung gab...


foofoobar schrieb:
Echt jetzt? Lass das doch einfach. Erspart uns beiden Ärger
Ergänzung ()

@foofoobar Ich hab zwar auch adhs, aber solche Provokationen müssen doch nicht sein.
 
Zuletzt bearbeitet:
kali-hi schrieb:
Du hast "sicherstellen" zu streng interpretiert. Genauer gesagt, bekomme ich eine Mitteilung per Messenger vom anderen Server Menschen, dass sich eine Ressource geändert hat. Weil Menschen aber Fehler machen können, kann es sein, dass sich tatsächlich noch nichts geändert hat. Für diesen seltenen und unwahrscheinlichen Fall muss ich die Inhalte vergleichen können, um sicherzustellen, dass es tatsächlich eine Änderung gab.
Deshalb ist es sinnvoll, den konkreten Anwendungsfall zu benennen und nicht nur Detailfragen zum eigenen Ansatz.


kali-hi schrieb:
Echt jetzt? Lass das doch einfach. Erspart uns beiden Ärger
Sieht doch nach einem guten Beitrag aus?? PS.: Achso, der Pfad. Ist das ein Insider zwischen euch beiden? Dann sollten ihr mehr raus gehen anstatt hier rum zu hängen.
 
  • Gefällt mir
Reaktionen: madmax2010 und kali-hi
kali-hi schrieb:
old_file_1 scheint 2,5 Stunden älter zu sein als new_file_1, obwohl es keine Änderung gab...
Hab es schon herausgefunden, das neue File (https://new_file_1) ist noch einmal gezippt, dann nützt das mit Timestamps alles nicht...

Gut, aber das soll jetzt nicht mehr Problem in diesem Thema sein, denn die eigentliche Frage ist ja schon beantwortet...
 
So ginge es, insofern man nicht noch weiter vereinfachen kann:

Bash:
#!/bin/bash
set -e
if [ ! "$BASH_VERSION" ]; then
    echo "Please do not use sh to run this script ($0), just execute it directly" 1>&2
    exit 1
fi
if [ -f file1.zip ]; then
    d1=$(date -r file1.zip)
    d1=$(date -d "$d1" +%s)
    d2=$(curl -sI https://file1.zip | grep Last-Modified)
    d2=$(date -d "${d2:15}" +%s)
    echo "Old zip: $(date -d @"$d1")"
    echo "New zip: $(date -d @"$d2")"
    if [ "$d1" -ge "$d2" ]; then
        echo "Nothing to do."
        exit 0
    fi
    rm -v file1.zip
fi
#...
wget https://file1.zip
unzip file1.zip
date >> update_log.txt
mysum1=$(sha256sum file1)
mysum1_short=$(echo "${mysum1}" | cut -c1-32)
mysum2=$(sha256sum dir/file1)
mysum2_short=$(echo "${mysum2}" | cut -c1-32)
echo "$mysum1" >> update_log.txt
if [ "$mysum1_short" = "$mysum2_short" ]; then
    echo "Warning: The versions are identical!"
    rm -v file1
else
    mv -v file1 dir/
fi
#...

BeBur schrieb:
Dann sollten ihr mehr raus gehen anstatt hier rum zu hängen.
Das mache ich jetzt auch.
 
  • Gefällt mir
Reaktionen: BeBur
kali-hi schrieb:
Hab es schon herausgefunden, das neue File (https://new_file_1) ist noch einmal gezippt, dann nützt das mit Timestamps alles nicht...
*.zip enthält CRCs, da könnte es reichen nur die ersten n Bytes runterzuladen und die CRC zu vergleichen und ein Timestamp ist da auch drin.
 
  • Gefällt mir
Reaktionen: kali-hi, dms und madmax2010
Bash:
#!/usr/bin/env bash
URL="$1"
LOCAL="$2"
N_BYTES="${3:-65536}"  # Standard: 64 KiB

TMP_REMOTE=$(mktemp)
trap 'rm -f "$TMP_REMOTE"' EXIT

echo "Lade erste $N_BYTES Bytes von $URL ..."
curl -s --range 0-$((N_BYTES-1)) -o "$TMP_REMOTE" "$URL"

CRC_LOCAL=$(head -c "$N_BYTES" "$LOCAL" | cksum | awk '{print $1}')
CRC_REMOTE=$(cksum "$TMP_REMOTE" | awk '{print $1}')

if [[ "$CRC_LOCAL" == "$CRC_REMOTE" ]]; then
    echo "Dateien identisch (CRC gleich)"
else
    echo "Dateien unterscheiden sich (CRC ungleich)"
fi

grob so dann
braucht noch error handling -> exisitiert die datei lokal, wenn nicht, 1x komplett laden; oder du packst die letzte(n) CRC(s) in eine Datei und vergleichst mit der letzten
 
madmax2010 schrieb:
grob so dann
braucht noch error handling -> exisitiert die datei lokal, wenn nicht, 1x komplett laden; oder du packst die letzte(n) CRC(s) in eine Datei und vergleichst mit der letzten
Ich meinte die CRC in dem zip:
Code:
$ unzip -lv zippy.zip
Archive:  zippy.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
  824774  Defl:N   820208   1% 2025-10-18 16:20 672b2bbe  tt
--------          -------  ---                            -------
  824774           820208   1%                            1 file
$

Aber der blöde unzip kann nicht von stdin lesen und einem "unzip -lv" reichen die ersten 2K eines zips nicht aus gibt eine Fehlermeldung
 
Zurück
Oben