Soo, etwas verspätet dann noch ein paar Erklärungen. Ich hoffe, ich sorge nicht für noch mehr Verwirrung
letzter3 hat geschrieben: 07.04.2022 00:24:29
legt die zu verwendende shell fest
Hmm, in gewisser Weise, ja. Man kann ein Shellskript auch immer ohne diese erste Zeile als
ausführen. Dann muss man aber wissen, welche Shell (häufige Frage: Bash oder nicht Bash) das Skript braucht. Macht man ein Skript ausführbar (z.B. mit
chmod +x) und zusammen mit dieser ersten
Shebang-Zeile übernimmt dann das Betriebssystem das Aufrufen der angegebenen Shell. Und übergibt dieser den Namen des abzuarbeitenden Skripts. Das Skript wird dann äquivalent zu den beiden obigen Zeilen aufgerufen, man kann es wie ein „richtiges“ Programm verwenden:
Die Shebang-Zeile ist nicht nur für Shellskripte relevant, sondern auch Python, Perl, awk, manchmal Makefiles etc. etc. Prinzipiell kann man in der Shebang-Zeile ein beliebiges Programm – einen Interpreter für diese (Skript-) Datei – angeben, das aufgerufen wird und den Namen der (Skript-)Datei übergeben bekommt.
letzter3 hat geschrieben: 07.04.2022 00:24:29
beenden wenn Nicht-Null-Status oder nicht gesetzte Variablen für die Variable pipefail
Verstehe ich nicht. pipefail wird doch nicht definiert?
pipefail ist eine
Option, die keine Kurzform wie das -e und -u hat. Was sie bewirkt, verrät dir
Ich hatte sie hier gesetzt, damit (in Kombination mit -e) das
ganze Skript fehlschlägt, wenn in den beiden Funktionen zum Umwandeln von PDF zu Text etwas scheitert. (Die beide eine Pipeline
… | … benutzen.)
letzter3 hat geschrieben: 07.04.2022 00:24:29
Hmmm. destname-Erklärung im eigentlichen Sinne nicht gefunden.
Das ist einfach eine Funktionsdefinition, „destname_pdftotext“ ist der volle Funktionsname. Man kann es auch als
schreiben, aber das ist Bash-spezifisch und m.M.n. unnötig viel zu tippen. Die Funktion würde in anderen Sprachen
ganz grob so aussehen, vielleicht hilfts mit Signatur:
Code: Alles auswählen
# C++
std::string destname_pdftotext(const std::string& pdf_path);
# C
int destname_pdftotext(const char *pdf_path, char *str, size_t len);
# Python
def destname_pdftotext(pdf_path: str) -> str
letzter3 hat geschrieben: 07.04.2022 00:24:29
Code: Alles auswählen
{
pdftotext "$1" - | grep -m1 "^\*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_.*\*$" | tr -d '*'
}
Wende pdftotext auf das Dokument (welches?) an. Greppe dann bis die Zahl 1 mal gefunden wurde (-m1).
Ratemodus für "^\ -> alles innerhalb der *
Sammle nur Ziffern 0-9, für YYYY-MM-DD
Setze _
Das ist nun der Inhalt der Funktion und mehr oder weniger die einzelnen Schritte zusammengeschrieben, die ihr vorher schon ausprobiert hattet. Beide Funktionen hier werden (weiter unten) mit dem Pfad einer PDF-Datei als Argument aufgerufen:
In anderen Sprachen wäre das Argument oft in Klammern, hier nicht. Das Argument wird dann über
$1 verwendet, da es das 1.
Funktionsargument ist. Die Funktionsargumente werden in Skripten nicht per
Namen (wie das
pdf_path in den drei Beispielen oben), sondern nur über ihre
Position beim Funktionsaufruf angesprochen.
In der Funktion wird also als erstes pdftotext mit der PDF-Datei aufgerufen. Das Minus als zweites Argument ist eine oft benutzte Konvention, die dem Programm sagt, dass es sein Ergebnis nicht in eine Datei schreiben soll, sondern direkt auf die
Standardausgabe. pdftotext kann das praktischerweise auch. Damit spart man sich hier eine temporäre Datei, die man anschließend wieder löschen müsste.
Code: Alles auswählen
… | grep -m1 "^\*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_.*\*$"
Die Ausgabe von pdftotext – der Text des PDFs – wird über eine
Pipe | direkt an grep weitergegeben. Deshalb sieht man die Ausgabe von pdftotext auch nicht tatsächlich auf dem Bildschirm. Den Suchausdruck hatte ich, meine ich, von euren vorherigen Schritten kopiert. Genau, das -m1 sorgt dafür, dass höchstens ein grep-Ergebnis weitergegeben wird. Ähnlich wie ein
head -1, was im Thread auch auftauchte. Ist ein Baustein, um fehlerhafte Umbenennungen auszuschließen.
Das tr entfernt abschließend noch die Sternchen vorne und hinten, die Zeile im PDF sah ja so aus:
*2022-03-24_20123988*. Und ohne die Sternchen ist es ja praktischerweise direkt der Zieldateiname. (Oder hab ich dabei was übersehen?)
letzter3 hat geschrieben: 07.04.2022 00:24:29
es verlässt mich
Dran bleiben!
letzter3 hat geschrieben: 07.04.2022 00:24:29
Frage: kann anstelle von [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] auch [:digit:]-[:digit:]-[:digit:] verwendet werden?
Fast.
[:digit:] ersetzt
ein 0-9. Man müsste also (außerdem mit zwei
[[ … ]], warum, erklärt Meillo bestimmt parallel bald
)
Code: Alles auswählen
[[:digit:]][[:digit:]][[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]
schreiben, was gleich nicht mehr unbedingt kürzer ist.
grep kennt auch eine Angabe, wie oft sich ein Ausdruck wiederholen soll
Code: Alles auswählen
grep -E '[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}'
aber auch da wäre
[0-9], statt
[[:digit:]] kürzer und wohl auch übersichtlicher.
letzter3 hat geschrieben: 07.04.2022 00:24:29
Code: Alles auswählen
destname_zbarimg()
{
zbarimg -q -Sdisable -Scode39.enable <(pdftoppm -png -singlefile "$1") | cut -d: -f2-
}
Lege Ziel fest->hier die Funktion (?) zbarimg
Wende zbarimg an auf den input, der von pdftoppm kommt. Verwende keinen Barcode->verwende code39 und gebe nur das Barcode-Ergebnis aus.
pdftoppm erzeugt ein PNG und wandelt nur die erste Seite in ein png.
Benutze (nach zbarimg???) einen TRENNER anstelle des Tabulators und nur für die Felder 2 (???)
Also gib nur die 2 gefundenen (Datum _ Variable) aus?
Wiederum eine Funktion, mit Namen „destname_zbarimg“, die diesmal (offensichtlich) zbarimg benutzt und wieder hauptsächlich die vorher im Thema ausprobierten Einzelschritte kombiniert. Die Optionen für zbarimg und pdftoppm sind einfach übernommen. Bei zbarimg explizit nur Code 39 zu aktivieren kann mögliche Falscherkennungen anderer Codes vorbeugen, denke ich.
Ich habe aber wieder einen „Trick“ benutzt, um keine temporäre, von pdftoppm produzierte PNG-Datei zu haben, die man löschen müsste. pdftoppm bekommt keinen Zieldateinamen übergeben – sondern nur die Quell-PDF-Datei in
$1 – und schreibt sein Ergebnis (die Bits einer PNG-Datei) damit auf die Standardausgabe. Folgende Syntax sorgt dafür, dass die Bash (geht nur mit der) automatisch selbst dafür sorgt, dass diese Standardausgabe an zbarimg wie eine Datei übergeben wird:
Das erreicht das gleiche wie das, was du weiter oben schon mit einer temporären PNG-Datei ausprobiert hattest (leicht von mir umformatiert):
letzter3 hat geschrieben: 02.04.2022 14:06:11
Code: Alles auswählen
pdftoppm -png -singlefile some.pdf tmp
zbarimg -q -Scode39.enable tmp.png
Die Ausgabe von zbarimg ist für die relevanten PDFs ja anscheinend
letzter3 hat geschrieben: 02.04.2022 14:06:11
Das
sorgt am Ende nur dafür, dass das
CODE-39: weggeschnitten wird.
cut unterteilt die Zeile in einzelne Felder, als Trennzeichen (
delimiter) wird
: angegeben. Man hat also „CODE-39“ und „2022-03-24 20123988“ und möchte davon jetzt nur das 2.
Feld haben:
-f2-. Das Minus hinter der 2 war eigentlich überflüssig, es besagt „2. Feld und alle folgenden“ – es gibt ja keine weiteren.
Ergänzung: An der Stelle hab ich den Ursprungsbeitrag gerade noch ergänzt und ein
angehängt. Das ersetzt das eine Leerzeichen im „2022-03-24 20123988“ durch den gewünschten Unterstrich.
letzter3 hat geschrieben: 07.04.2022 00:24:29
den : hab ich auf die Schnelle nur als "application" in der bash gefunden. Die englische Erklärung ist mir aber zu hoch. Setze incl. in der Methode (hier->zbarimg) verwendeten Variablen und führe aus? Würde dann mit dem destname korrespondieren.
Da war ich etwas übermütig
und die method-Variable/-Auswahl an sich mag für dein Problem am Ende auch überflüssiug sein. Diese Zeile weist der Variablen den Standardwert „zbarimg“ zu, wenn die Variable noch ungesetzt/null oder leer ist. Lang – aber nicht 100% äquivalent! – ausgeschrieben:
: ist ein Kommando, das einfach nichts macht, siehe
help :. Kann man als Platzhalter oder zum Verwerfen von Dingen benutzen. Die Syntax
weist
variable den Wert
wert zu, wenn
variable ungesetzt oder null ist. Das Konstrukt gibt allerdings den Wert von
variable auch zurück, deshalb verwirft man ihn mit dem
:.
letzter3 hat geschrieben: 07.04.2022 00:24:29
Quellfile (ohne Prüfung, ob das überhaupt ein PDF ist?)
Das
$1 ist hier des erste Argument, das dem Skript übergeben wurde. Da das noch keine Komplettlösung sein sollte, hab ich da keine weitere Prüfung eingebaut. Aber hast recht, das sollte man sicher irgendwo noch machen.
letzter3 hat geschrieben: 07.04.2022 00:24:29
Zielverzeichnis. Ist von mir zu setzen. Aber bei {2: steig ich aus.
Eine andere Variante für Standardwertzuweisung, wenn ungesetzt. Nur das hier der Inhalt des zweiten Skriptarguments,
$2, angeguckt wird und wenn leer oder nicht angegeben der Vorgabewert
/default/destination/dir der Variablen
destdir zugewiesen wird. Anderfalls wird der Inhalt von
$2 dem
destdir zugewiesen. Du kannst das Skript also mit oder ohne aufrufen:
Code: Alles auswählen
~$ das_skript ein.pdf
~$ das_skript ein.pdf /der/zielordner
Hatte ich mehr zum Ausprobieren so geschrieben, am Ende reicht dir vielleicht
letzter3 hat geschrieben: 07.04.2022 00:24:29
Definition des künftigen Namens in Abhängigkeit der verwendeten Methode. Defaultmäßig also des outputs von cut -d: -f2-, nachdem zbarimg an dem Quellfile rumgewurschtelt hat.
Hier wird eine der beiden oben definierten Funktionen aufgerufen, abhängig von gesetzter
method. Die Ausgabe beider Funktionen ist so zurechtgeschnitten, wie oben beschrieben, dass sie genau der gesuchte Zieldateiname ist, plus das hier noch angehängte
.pdf.
ist wieder eine Abkürzung, um abhängig vom
method-Inhalt die richtige Funktion aufzurufen. Ausgeschrieben wäre es:
Code: Alles auswählen
if [ "$method" = pdftotext ]; then
destname_pdftotext "$srcfile"
elif ["$method" = zbarimg ]; then
destname_zbarimg "$srcfile"
fi
Der Name eines aufzurufenden Kommandos kann nämlich auch einfach in einer Variablen stehen:
letzter3 hat geschrieben: 07.04.2022 00:24:29
Code: Alles auswählen
echo "Would move '$(realpath "$srcfile")' to '${destdir%/}/${destname}'"
Schreibe auf den screen, was du tun würdest.
Ja, genau. Einfach zum Ausprobieren für dich (und mich vorher).
letzter3 hat geschrieben: 07.04.2022 00:24:29
Aber wo kommt jetzt realpath her?
Das ist ein Kommando aus den
coreutils. Es gibt dir zu einem Dateipfad den absoluten Pfad aus:
Code: Alles auswählen
~$ realpath Documents/some.pdf
/home/letzter3/Documents/some.pdf
War hier nur zu kosmetischen Zwecken verwendet.
letzter3 hat geschrieben: 07.04.2022 00:24:29
Auskommentiert wird das Quell-PDF verschoben in das Zielverzeichnis als PDF mit dem Namen der durch destname=$("destname_$method" "$srcfile").pdf festgelegt wurde.
Genau,
# entfernen und das Verschieben würde, nach obigem echo, tatsächlich ausgeführt. Wenn ich keinen Quatsch gemacht hab, dann so wie du es in deinen Beiträgen beschrieben hast. (Gut, dass dir hier das
${destdir%/} nicht aufgefallen ist. Deshalb schreib ich jetzt auch nix mehr dazu
)
letzter3 hat geschrieben: 07.04.2022 00:24:29
Was mir noch nicht einleuchtet: wo liegt das script? Es wird ja kein Verzeichnis angegeben, in dem "gearbeitet" werden soll.
Was ich vielleicht mehr am Anfang meines Romans hier hätte schreiben sollen: Das Skript war erstmal nur als Zusammenfassung deiner und eggys Versuchsschritte vorher gedacht. Es fehlt – offensichtlich, hoffe ich – noch der automatisierte Aufruf durch inotify, cron oder den Hausmeister. Wenn ich mich richtig erinnere, sollte das Skript gelegentlich auch manuell aufgerufen werden (?) (grad keine Lust mehr, zu suchen …). Dann würde es sich anbieten, dieses Umbenennen so als eigenständiges Skript zu halten und das autmatisierte Aufrufen separat zu lösen – und dieses Skript dort heraus aufzurufen.
Aber zu der Frage: Du kannst es hinlegen, wo du möchtest. Temporäre Dateien sind vermieden, wie beschrieben (die landen also nicht
irgendwo, da es keine gibt). Das Zielverzeichnis kann dem Skript übergeben oder fest definiert werden. Und an die PDF-Dateien kommt es heran, indem ihre Pfade sinnvoll übergeben werden. Wenn es also automatisiert aufgerufen würde, wären das sehr wahrscheinlich sowieso absolute Pfade:
Code: Alles auswählen
rename_from_barcode /some/samba/share/1970-01-01-00-00-00.pdf
rename_from_barcode wäre z.B. ein Name der sich anbietet. Ich würde das Skript wahrscheinlich nach
/usr/local/bin tun, also
/usr/local/bin/rename_from_barcode.
letzter3 hat geschrieben: 07.04.2022 00:24:29
Kann
a) ein Log erzeugt werden, in dem stumpf die Umwandlung dokumentiert wird?
b) eine Warnmeldung per mailx bei Abbruch versandt werden?
Ja, ein Log wär nicht schwer. Man könnte einfach das
echo nach
/var/log/irgendwo umleiten oder
logger benutzen. Mail verschicken ebenso, da kenn ich mich aber mit dem üblichen Werkzeugen nicht aus. Beides würde ich instinktiv und aus Erfahrung eher in der evtl. separaten Automatisierung machen, nicht hier direkt.
Wenn ich das so vorschlagen darf, du hattest ja geschrieben, dass du dich Ostern damit wieder beschäftigen willst: Du könntest ausprobieren, ob meine „Zusammenfassung“ die richtigen Zieldateinamen produziert, falls wichtig mit beiden Methoden.
So, Bierchen ist leer, das muss erstmal reichen