[SOLVED] Dateien zusammenführen, mit Dateiname und mehr


#1

Ich habe zirka 320 txt-Dateien die ich in eine Datei kopieren will. Grundsätzlich einfach über den Terminal (ls > NeueDatei.txt). Doch ich will ob jedem Text den Dateinamen stehen haben. Was zusätzlich toll ist, wenn vor und nach dem Dateinamen ein einheitlicher, vorgegebener Text mit eingetragen werden kann.

Welche Befehle/Parameter kann ich dazu verwenden (sofern es überhaupt durchführbar ist)? Gibt es ein gutes Werkzeug das dies (und vielleicht noch mehr) ermöglicht?


#2

Wie wäre es mit einem kleinen Bash Script. Einfach eine for Schleife über alle Text Dateien laufen lassen.

http://openbook.rheinwerk-verlag.de/shell_programmierung/shell_006_009.htm#RxxKap00600904004E2B1F04E172


#3

Hallo rene,
wie xabbu schon sagte, for-Schleife.
Da es mir bei sowas immer in den Fingern juckt, mein Vorschlag:
for f in *.txt; do echo $f >> alles.txt; cat $f >> alles.txt; done
das ist natürlich die simpelste Variante, stark verbesserungswürdig. Aber irgendwas soll ja für dich auch noch übrigbleiben :wink: Der vorgegebene Text z.B.
Zum Schluss landet alles in alles.txt.

viele Grüße gosia


#4

@xabbu, @gosia: Ihr seid einfach Spitze! :wink: Nach xabbu habe ich bereits begonnen mich in Bash Script einzulesen. Danke gosia auch für Deinen tollen ersten Hinweis. :vulcan_salute:


#5

Ob man das ganz Buch lesen muss weis ich nicht. Es könnte etwas “trocken” sein.
Aber wenn du längerfristig Linux verwenden willst, ist es sehr hilfreich etwas über die Bash und Shell Scripting zu lernen.
Es ist gut, dass du ein Problem hast welches mit einem Shell Script relativ einfach gelöst werden kann. Wichtig ist das du anfängst und ein wenig experimentierst. Dann klappt es mit den Grundlagen meist recht schnell.


#6

So Freunde, ich habe erstmal eine Lösung für mich gebaut (@gosia: Jetzt verstehe ich auch Deinen Vorschlag :wink:):

#!/bin/bash
for datei in *.txt
do
dateiname=$(basename – $datei .txt)
echo -e “\n\n\TextVorher $dateiname TextDanach” >> alles.xxx
cat $datei >> alles.xxx
done

Im Nachhinein, nachdem ich mich (über-)fliegend in Bash bzw. Shell-Script etwas eingelesen habe, finde ich es mehr als spannend. Ich greife immer mehr zum Terminal. :sunglasses::vulcan_salute:


#7

Hallo rene,

Das ist genau die richtige Einstellung :slight_smile:
Nur zur Ergänzung, dein Skript hat noch einen Syntaxfehler (s. basename) oder ist es nur ein Druckfehler?

viele Grüße gosia


#8

@gosia: Ich hoffe es ist nur ein Druckfehler… :wink: Das Script liefert bei meinen drei Testdateien (eins.txt | zwei.txt | drei.txt) folgendes (Die Zahlenblöcke sind die Inhalte der Testdateien):

TextVorher drei TextDanach
333
333
333

TextVorher eins TextDanach
111
111
111

TextVorher zwei TextDanach
222
222
222

Genau das was ich haben wollte …und da ist für mich zum experimentieren noch gaaaanz viel Luft nach oben offen. :slightly_smiling_face: @gosia, @xabbu: …übrigens, ein herzlicher Rutsch ins 2019 für Euch! …und danke vielmals für Eure Unterstützung! René


#9

Hallo René,

Dann wird es wohl so sein. Ich meinte den Bindestrich (oder Minus) hinter basename. Dürfte da nicht sein und wirft bei mir jedenfalls einen Syntaxfehler.
basename: zusätzlicher Operand „.txt“

vielen Dank, wünsche ich dir und allen anderen auch.

viele Grüße gosia


#10

@Gosia: Stimmt, beim kopieren aus dem Posting steht da bei mir im Texteditor auch nur noch ein Bindestrich, im Original sind es zwei. Auch die Anführungszeichen werden falsch kopiert und erzeugen einen Fehler.

Mein Fehler; Zur Darstellung des Codes habe ich “Blockquote” verwendet, “Preformatted Text” wäre die klügere Wahl gewesen - gut zu wissen.

Eben getestet:
In Blockquote kopiert: "--"
Aus Blockquote kopiert: “–”
“Preformatted Text” macht da kein Problem. :wink:

Somit hier nochmal:

#!/bin/bash
for datei in $@
do
 dateiname=$(basename -- $datei .txt)
 echo -e "\n\n\section*{$dateiname}" >> AlleTexte.txt
 cat $datei >> AlleTexte.txt
done

Zudem habe ich noch eine kleine Erweiterung eingebaut: for datei in *.txt wurde zu for datei in $@. Damit kann ich beim Scriptaufruf den Pfad zu den Textdateien als Parameter übergeben.

Jetzt suche ich noch nach der Möglichkeit der Sortierung in der Schleifenanweisung. Als Standard wird nach Alphabeth sortiert, ich benötige beim Einlesen der Dateien die Sortierung nach Datum (ls -r -t). :thinking::wink:


#11

Ich wünsche dir und euch allen auch einen guten Rutsch.

Naja die zwei Bindestriche können helfen, so oft fangen Dateinamen aber nicht mit einem Bindestrich an. Was aber schon öfter vorkommt sind Computer Benutzer die Leerzeichen in ihre Dateinamen einbauen. Gegen so etwas sollte man beim Scripte schreiben Vorkehrungen treffen.

Die for Schleife kann auch Befehle ausführen um an eine Liste mit Werten zu kommen.

...
for datei in `ls -rt`
do 
...

Solange deine Datei Namen keine Leerzeichen enthalten würde das ganz gut klappen. Falls es zu erwarten ist, dass Leerzeichen vorkommen könnten, füge die Option -1 hinzu. (ls -tr1) und setze IFS=$'\n' . IFS ist eine Interne Variable und damit kann man ein wenig steuern wie z.B. die for Schleife einzelne Werte unterscheidet. Normalerweise ist es ein Leerzeichen. Mit IFS=$'\n' wird es zu einem Zeilenumbruch Zeichen.

Damit du immer noch einen Pfad als Parameter verwenden kannst, benutze einfach cd . Das funktioniert auch in Scripten. Ich persönlich würde jetzt nicht mehr die Sammlevariable nehmen, sondern $1 .

#!/bin/bash
cd "$1"

Wenn du mehrere Pfade als Parameter verwenden willst, klappt das natürlich so nicht mehr.


#12

@xabbu: Danke für Deine wertvollen Hinweise - ich experimentiere aktuell damit.

Nun habe ich noch eine andere (vielleicht etwas holprige) Version erarbeitet. :wink: …ich bin ja am experimentieren. Ich erstelle im ersten Schritt eine Liste aller txt-Dateien, sortiert nach meinem Wunsch, aus dem Verzeichnis das ich als Parameter dem Script übergebe ($@). Im zweiten Schritt lese ich diese Datei zeilenweise mit der while-Schlaufe aus und verarbeite diese wie zuvor:

#!/bin/bash
ls $@ -rt > Dateiliste.txt
while read Zeile
do
 dateiname=$(basename -- $Zeile .txt)
 echo -e "\n\n\section*{$dateiname}" >> AlleTexte.txt
 cat $Zeile >> AlleTexte.txt
done < Dateiliste.txt

Dateinamen mit Leerzeichen sind damit (noch) nicht gelöst. Doch für mich ist das Ziel (vorerst) erreicht. :wink::vulcan_salute: …für weitere Hinweise bin ich natürlich offen!


#13

Hallo rene,

Dateinamen mit Leerzeichen sind generell blöd, sollte man vermeiden. Aber wenn schon, dann helfen Anführungszeichen (oder die Lösung von xabbu, wie ich gerade sehe). Habe das mal in dein Skript gebaut:

#!/bin/bash
ls "$@" -rt>Dateiliste.txt
while read LINE
do
 dateiname=$(basename -- "$LINE" .txt)
 echo -e "\n\n\section*{$dateiname}" >> AlleTexte.txt
 cat "$LINE" >> AlleTexte.txt
done < Dateiliste.txt

Trotzdem ein paar Anmerkungen:
Das mit der Dateiliste.txt kann man machen, das zusätzliche Anlegen von Dateien sollte man aber vermeiden. Ist hier auch nicht notwendig, wie xabbu gezeigt hat

for datei in `ls -rt`

Ungünstig ist auch das Verwenden von $@, wenn Du hier ein Verzeichnis angibst, in dem nicht nur *.txt Dateien liegen wandern die alle in deine Dateiliste, was nicht gewollt ist. Als Parameter vielleicht die gewünschte Endung angeben, also txt. Man sollte bei Parametern immer vom schlimmsten ausgehen und sich irgendwie dagegen absichern, durch Tests o.ä. Spielt bei solch kleinem Skript keine grosse Rolle, aber besser wenn Du dich schon früh daran gewöhnst.

Ich hoffe, ich habe dich nicht zu sehr entmutigt, aber da Du extra um Hinweise gebeten hast.

viele Grüße gosia


#14

@gosia: Ja, ich mag Dateinamen mit Leerzeichen auch nicht (ist sowieso doof das das geht), ich versuche auch Umlaute in Dateinamen zu meiden.

Das Script rufe ich übrigens mit Pfad und Dateityp auf. Also so:
Scriptname gugus/gaga/*.txt

Die Anweisung von xabbu for datei in 'ls -rt' ruft bei mir immer (noch) einen Fehler hervor. Er meint dann irgendwie “ls” und “rt” seien Dateien, die er natürlich nicht finden kann. Da teste ich noch. $@ finde ich bisher ganz toll, da ich verschiedene Verzeichnisse verwenden muss. Danke für den Hinweis mit den "" die ich natürlich ebenfalls einbauen werde. Und Du entmutigst mich nicht gosia, es ist toll soviele Hinweise und Tipps zu erhalten. Danke! :slightly_smiling_face:


#15

@gosia: Funktioniert einwandfrei mit den "". Dateinamen mit Leerzeichen werden gelesen. :slightly_smiling_face:


#16

Es gibt verschiedene Zeichen die unterschieden werden müssen. Bei ' und " ist es noch einfach, aber es gibt ein drittes Zeichen ` . Wird manchmal als Backtick/backquote bezeichnet. Bei manchen deutschen Tastaturlayouts musst du die Taste zwei mal drücken, weil gewartet wird ob du nicht doch noch auf die e Taste drückst.

Du könntest auch dein ls noch ein wenig verfeinern, zum Beispiel mit ls -rt *.txt . Das Script dann mit gugus/gaga aufrufen und im Script einfach ein cd "$1"

Meinst du damit Scriptname gugus/gaga/*.txt gugus/gaga1/*.txt gugus/gaga2/*.txt oder Scriptname gugus/gaga/*.txt und Scriptname gugus/gaga1/*.txt
Beim letzteren solltest du wirklich auf $1 wechseln. $@ ist die Summe aller $1 und $2


#17

@xabbu: Ich verwende die zweite Variante, also Scriptname pfad-irgendwohin/*.txt und dann ein nächster Aufruf mit einem anderen pfad. Ich werde Deinem Hinweis folge leisten und $1 verwenden. Dennoch will ich fragen, was ist der Unterschied ob ich $@ oder $1 in meiner Version verwende? Das $@ die Summe aller $1, $2, … ist, ist ein spannender und wichtiger Hinweis für mich, Danke!

Der Unterschied bei der Nutzung Deiner eingangs gezeigten verschiedenen Anführungszeichen will ich im Auge behalten.:frog:


#18

Hallo rene,

Das liegt wahrscheinlich daran, dass ls -rt in Backquote eingeschlossen werden müssen, was man hier nicht so richtig sieht. Also nicht sowas ’ sondern Backquote (s. @xabbu -> wie hast Du das als Text hier reinbekommen?)

Kriegt man hier nicht so richtig hin, auch deshalb bevorzuge ich die neuere Version der Kommandosubstitution, die Du ja auch schon kennst:
for datei in $(ls -rt *.txt)
Für den Rest hat mich xabbu überholt :wink:

viele Grüße gosia


#19

Es gibt ein paar definierte Variablen die mit dem starten des Scriptes erzeugt werden.
http://openbook.rheinwerk-verlag.de/shell_programmierung/shell_004_008.htm#RxxKap00400804004DB41F038172

$0 ist der Script Name.
$1 ist der erste Parameter, $2 der zweite und so weiter.
$* sind alle Parameter als eine Zeichenkette
$@ sind alle Parameter als ein Array
$# die Anzahl aller Parameter

und noch viele mehr

./mein-script.sh parameter1 parameter2 "para meter3"

$0 = ./mein-script.sh
$1 = parameter1
$2 = parameter2
$3 = para meter3
$# = 3

$* = “parameter1 parameter2 para meter3”
$@ = “parameter1” “parameter2” “para meter3”

Der Unterschied zwischen$* und$@ ist ein bisschen schwer dazustellen. Den mit echo sehen die gleich aus. Aber im Prinzip besteht $@ aus einen Strings für jeden Parameter in einem Arry. $* ist hingegen ein zusammenhängender String. Der Unterschied ist häufig ziemlich egal, bis auf den Fall wo das Script mal nicht mehr so läuft wie gewünscht und komische Fehler erzeugt.

@gosia Ich habe <code> </code> Tages um den backquote verwendet. Aber eigentlich hast du natürlich recht das man bei modernen Shells eher $( ... ) verwenden sollte.


#20

@gosia, @xabbu: Hier will ich jetzt ein grosses DANKE an Euch beide aussprechen. Ich habe enorm viel von Euch gelernt …und mich zwischendurch gefragt, ob dieses Forum nur aus uns dreien besteht.:grin: …für Heute ist Feierabend!:vulcan_salute: …bis demnächst!