Fragen zur Bash in Debian
Glory
- sonstiges
Ich hoffe, dass hier ein paar Linux/Bash Experten zu finden sind ;)
Vorweg: Ich kenne mich mit der Bash kaum aus. (bin eigentlich die Eingabeaufforderung/batch von Windows gewöhnt)
Mein Ziel ist es, ein Verzeichnis per Bash-Datei nach weiteren Verzeichnissen (mit der Syntax "Name_$Nummer") abzusuchen und ein neues Verzeichnis mit der nächsthöheren nicht vorhanden Nummer zu erstellen.
Dazu habe ich mir überlegt, dass ich eine Zählschleife laufen lasse, die ein Verzeichnis nach dem Ordner mit Namen "beispiel_1" durchsucht und ihn, wenn sie ihn nicht findet, nicht anlegt und dann abbricht oder ansonsten den nächsten Durchlauf startet (mit "beispiel_2"), usw.
Mein bisheriges Script:
--------------------------------
#!/bin/bash
for ((i=1; $i<=9999; $i++)) #scheint schon fehlerhaft zu sein (obwohl auf einer Seite genau so gefunden)
do
curdir=find "beispiel$i" #funktioniert so auch nicht (alleine getestet)
if $curdir!="beispiel$i"
then mkdir "beispiel$i"
#else break <- kenne den Befehl nicht
done
#...
--------------------------------
Vielleicht könnt ihr mir ja ein bisschen unter die Arme greifen.
Wieso funktioniert z.B. das "curdir=find "beispiel$i"" nicht? Da wird anscheinend kein "find" ausgeführt sondern irgendetwas anderes.
Was aber eigentlich noch wichtiger ist: gibt es irgendwo eine wirkliche ausführliche Referenz? Die meisten, die ich bis jetzt gefunden habe, erklären die Syntax von IF, FOR, usw. nur sehr bruchstückhaft.
Und wie kann ich in der Bash selbst Hilfe finden?
Folgendes funktioniert nicht:
"for --help" oder "for -h" oder "for /?" oder "man for"
Hellihello
for dateipfad in ../Photobearbeitung/*.jpg
do
Dateiname=${dateipfad##*/}
echo "Dateiname: $Dateiname \n"
done
vielleicht kommst du so der sache näher?
vielleicht aber bist du mit PERL oder PHP insgesamt flexibler, wenn das neben der bash dort auch läuft?
Gruß,
frankx
Hellihello
code lang=apache
Warum benutzt du lang=apache?
for dateipfad in ../Photobearbeitung/*.jpg
do
Dateiname=${dateipfad##*/}
echo "Dateiname: $Dateiname \n"
donevielleicht kommst du so der sache näher?
»»
Leider nicht wirklich. Dort werden ja alle jpgs durchgegangen, ich will doch aber nicht *.jpg sondern beispiel_X, wobei X eine Nummer ist. Und wer weiß, ob der das alphabetisch macht?
Und was bedeutet
"Dateiname=${dateipfad##*/}"
?
vielleicht aber bist du mit PERL oder PHP insgesamt flexibler, wenn das neben der bash dort auch läuft?
Ich möchte das per Bash machen, weil ich es 1. lernen möchte, weil es 2. auch laufen soll, wenn PHP und PERL nicht zu Verfügung stehen (auch wenn ich es in PHP schnell lösen könnte) und weil man es schnell anpassen kann. (was ich tun werde)
Aber danke schonmal für deine Hilfe.
Hellihello Glory
Warum benutzt du lang=apache?
weil lang=shell keine wirkung hatte (;-)
for dateipfad in ../Photobearbeitung/*.jpg
do
Dateiname=${dateipfad##*/}
echo "Dateiname: $Dateiname \n"
donevielleicht kommst du so der sache näher?
»»Leider nicht wirklich. Dort werden ja alle jpgs durchgegangen, ich will doch aber nicht *.jpg sondern beispiel_X, wobei X eine Nummer ist.
Nun, es war als ansatz gedacht. *.jpg lässt sich ja durch * ersetzten.
Und wer weiß, ob der das alphabetisch macht?
Und was bedeutet
"Dateiname=${dateipfad##*/}"
?
wieso das funktioniert, weiss ich nicht, es gibt aber in dem fall den Dateinamen aus, bei "*" statt "*.jpg" würde ich mal tippen, den namen der enthaltenen Files (also auch Folder, denn in Linux ist doch "everything is a file" oder?).
Ich möchte das per Bash machen, weil ich es 1. lernen möchte, weil es 2. auch laufen soll, wenn PHP und PERL nicht zu Verfügung stehen (auch wenn ich es in PHP schnell lösen könnte) und weil man es schnell anpassen kann. (was ich tun werde)
o.g. wäre doch aber zumindest für deine for-schleife ein ansatz, auch was die syntax angeht.
Gruß,
frankx
Hellihello
for Ordnerinhalt in "name_"*
do
echo "Datei oder Verzeichnis: $Ordnerinhalt"
echo " - - - "
done
echo "ich habe fertich"
gibt erstmal alle Dateien mit "name_100" oder "name_101" etc. aus.
Gruß,
frankx
Hellihello
nach eingigem Probieren, habe ich ein paar dateien angelegt
touch name_100
touch name_101
das script dann
hoechste_nummer=100
for Datei_name in "name_"*
do
echo ---
echo "Datei mit Besandteil name_: $Datei_name"
#number_position=`expr index "$Datei_name" "_" - 1`
position_underline=`expr index "$Datei_name" "_" - 0`
length=`expr length "$Datei_name"`
echo Position des Underline ist: $position_underline, Länge ist: $length, die Zahl ist ${Datei_name:position_underline:length}
echo ---
nummer_teil_des_dateinamens=${Datei_name:position_underline:length}
#echo ${Datei_name:position_underline:length}
echo nummer_teil_des_dateinamens: $nummer_teil_des_dateinamens
if [ "$nummer_teil_des_dateinamens" == "102" ]
then echo 102, hallo!
else echo nicht 102, wasanderes
fi
if [ "$hoechste_nummer" -lt "$nummer_teil_des_dateinamens" ]
then echo hey, ich bin ja kleiner
echo nicht mehr lange
hoechste_nummer=$nummer_teil_des_dateinamens
echo jetzt bin ich $hoechste_nummer
else echo ich bin nicht kleiner
fi
echo ---
done
hoechste_nummer=`expr $hoechste_nummer + 1`
echo jetzt ist die hoechste nummer: $hoechste_nummer
neuer_dateiname="name_$hoechste_nummer"
echo neuer dateiname wäre: $neuer_dateiname - könnte jetzt mit touch erstellt werden
touch $neuer_dateiname
echo ist erstellt worden $neuer_dateianme, siehe listing:
ls -l name_*
echo "ich habe fertich"
bei mir "funzt" es erstmal, die prinzipien sind erkennbar.
s.a. http://www.chemie.fu-berlin.de/chemnet/general/topics/scripts_sh.html, http://tldp.org/LDP/abs/html/
Gruß,
frankx
Hallo
Ich hoffe, dass hier ein paar Linux/Bash Experten zu finden sind ;)
als Experten würde ich mich nicht bezeichnen, aber ich wohne bestimmt außerhalb der Stadt, bin somit Fachmann [1]:-)
Vorweg: Ich kenne mich mit der Bash kaum aus. (bin eigentlich die Eingabeaufforderung/batch von Windows gewöhnt)
Dann sollte das doch kein so großes Problem darstellen :-)
Mein Ziel ist es, ein Verzeichnis per Bash-Datei nach weiteren Verzeichnissen (mit der Syntax "Name_$Nummer") abzusuchen und ein neues Verzeichnis mit der nächsthöheren nicht vorhanden Nummer zu erstellen.
Mein Lösungsvorschlag geht davon aus, dass alle Verzeichnisse, die mit dem Suchmuster übereinstimmen anschließend eine Zahl als Suffix besitzen. Bestimmt geht es auch anders, wahrscheinlich auch eleganter, aber hier mein Vorschlag:
1. Schritt: Suche alle Verzeichnisse, die mit dem Suchmuster übereinstimmen.
2. Schritt: Betrachte davon nur den Zahlanteil nach dem Suchmuster
3. Schritt: Sortiere diese Zahlen
4. Schritt: Betrachte nur die größte Zahl
5. Schritt: Erhöhe die diese Zahl um 1
6. Schritt: Baue den neuen Verzeichnisnamen zusammen: Muster + Zahl
7. Schritt: Erstelle das neue Verzeichnis mit dem ermittelten Namen
1. Schritt:
Während der DIR-Befehl von CMD.EXE und COMMAND.COM einen Schalter besitzt, der das Ergebnis auf Verzeichnisse einschränkt, gibt es meines Wissens kein Gegenstück bei ls. Wir müssen uns mit einem Trick behelfen
ls -d1 beispiel_*/
listet nur die Unterverzeichnisse im aktuellen Verzeichnis aus, die mit beispiel_ beginnen.
-d sorgt dafür, dass im aktuellen Verzeichnis geblieben wird
-1 damit jeder Eintrag eine Zeile einnimmt.
*/, weil Verzeichnisse auf / enden :-)
2. Schritt:
Betrachte nur den Zahlenanteil in diesen Verzeichnisnamen. Dazu sind zwei Teilschritte erforderlich: Abschneiden des Präfixes "beispiel_" und Abschneiden des Slashes am Ende des Verzeichnisnamens. Für beides können wir das Kommando
cut
verwenden: Mit
ls -d1 beispiel_*/ | cut -d '/' -f 1
reichen wir die Ausgabe weiter an cut.
-d '/' sorgt dafür, dass am Zeichen '/' aufgetrennt wird. Dieses ist ein in Dateinamen verbotenes Zeichen, es kann somit nur am Ende vorkommen.
-f 1 besagt, dass der erste Abschnitt genommen wird, das ist der vor dem Slash am Ende.
Nun müssen wir noch das Präfix abschneiden. "beispiel_" hat 9 Buchstaben, also nehmen wir nur alles ab dem 10. Zeichen:
ls -d1 beispiel_*/ | cut -d '/' -f 1 | cut -c 10-
Wir schicken einfach das Ergebnis wieder an cut und übergeben den Schalter und Parameter, um alles ab dem 10. Zeichen zu bekommen.
3. Schritt: Sortieren
Zum Sortieren schicken wir das alles an das Kommando sort
ls -d1 beispiel_*/ | cut -d '/' -f 1 | cut -c 10- | sort -g
Der Schalter g sorgt für numerische Sortierung, genau das was wir wollen. Die höchste Zahl steht in der letzten Zeile.
4. Schritt: Größte Zahl
Die letzte Zeile bekommen wir mit tail
ls -d1 beispiel_*/ | cut -d '/' -f 1 | cut -c 10- | sort -g | tail -n 1
Mit dem Schalter n kann man die Zahl der angezeigten Zeilen festlegen. Wir brauchen nur die letzte.
5. Schritt: Erhöhen um 1
Den Wert, den der Befehl zurückliefert bekommen wir über $(Befehl) und wenden auf diesen Shell-Arithmetik an, siehe auch dieses Archivposting.
echo $[1+$(ls -d1 beispiel_*/ | cut -d '/' -f 1 | cut -c 10- | sort -g | tail -n 1)]
gibt die nächstgrößere Zahl aus
6. Schritt: Neuer Verzeichnisname
echo "beispiel_"$[1+$(ls -d1 beispiel_*/ | cut -d '/' -f 1 | cut -c 10- | sort -g | tail -n 1)]
gibt den neuen Verzeichnisnamen aus.
Wenn wir das ganze nun in ein Shellskript packen, können wir mit Variablen arbeiten, die Länge der Zeichenkette bestimmen und damit das ganze flexibler halten:
#! /bin/bash
MUSTER='beispiel_' # Weise der Variablen MUSTER die Zeichenkette zu,
# die den Anfang des Dateinamens bildet.
ANZAHL=${#MUSTER} # In der Variablen ANZAHL steht nun die Anzahl der
# Zeichen der Zeichenkette in der Variablen MUSTER
# d.h. die Stringlänge von MUSTER
TRENNER=$[$ANZAHL+1] # Wir benötigen die Zeichen _nach_ MUSTER
# Ermittle die höchste als Suffix vergebene Zahl in den Verzeichnisnamen
ZAHL=$(ls -d1 ${MUSTER}*/ | cut -d '/' -f 1 | cut -c ${TRENNER}- | sort -g | tail -n 1)
ZAHL=$[1+$ZAHL] # Ermittle die nächsthöhere Zahl
VERZEICHNIS=$MUSTER$ZAHL # Ermittle den neuen Verzeichnisnamen
mkdir $VERZEICHNIS # Erstelle das neue Verzeichnis
Nun kann man die meisten einzelnen Anweisungen einsparen, der Code wird dadurch weder schöner noch übersichtlicher - und Geschwindigkeit ist sowieso kein Thema:
#! /bin/bash
MUSTER='beispiel_' # Weise der Variablen MUSTER die Zeichenkette zu,
# die den Anfang des Dateinamens bildet.
# Mit dem Backslash am Ende einer Zeile kann man eine Anweisung auf
# mehrere Zeilen verteilen ...
mkdir $MUSTER$[1+$(ls -d1 ${MUSTER}*/ \
| cut -d '/' -f 1 \
| cut -c $[1+${#MUSTER}]- \
| sort -g \
| tail -n 1)]
sollte es tun. Getestet mit GNU bash, version 3.1.17(1)-release.
(Abtippfehler sind möglich, copy & paste war nicht möglich ...)
Du solltest sehen, wo Du eventuell eine Parameterübergabe einbauen könntest,
Du solltest verstehen, dass das Skript derzeit nur in dem Verzeichnis funktioniert, in dem gesucht werden soll.
Du solltest es mit einer Fehlerbehandlung ausstatten und an Deine Bedürfnisse anpassen und so abändern, dass Du es in Deinem persönliches bin-Verzeichnis abspeichern kannst.
Dabei wünsche ich Dir viel Erfolg.
Verbesserungsvorschläge werden gerne entgegengenommen.
Freundliche Grüße
Vinzenz
[1] Arthur Bloch, Murphys Gesetze, Die Regel des Mars
Hellihello
cool, der König der Scripte. Hatte mal ein schönes Batch von Dir.
.
(Abtippfehler sind möglich, copy & paste war nicht möglich ...)
??? Warum das nicht. Mit einer Markierung in der shell landet doch der markierte Inhalt automatisch in der Zwischenablage (dachte ich bzw. ist bei mir so...)
Verbesserungsvorschläge werden gerne entgegengenommen.
Ühö, morgen vielleicht, oder nächstes Jahr (;-)
Dank und Gruß,
frankx
Wow, ich hatte mein Script zwar schon fertig, aber sich extra soviel Zeit zu nehmen, ist sehr edel von dir! :)
Funktioniert auch tadellos (besser) als meins, welches folgendermaßen aussah:
--------------------------------
#!/bin/bash
dirName="ordner";
for (( i=99; i>= 1; i-- ))
do
find "$dirName$i";
isDir=$?;
if [ $isDir != 1 ]
then
let i=i+1;
mkdir "$dirName$i";
break;
else
continue;
fi
done
-------------------------------
Mein Script funktioniert natürlich nur, wenn es nicht mehr als 99 Ordner werden (werden es aber auch nicht).
Trotzdem vielen Dank! Und natürlich auch vielen Dank an frankx (ich glaubte mich schon aufgegeben).
zwei Fragen bleiben mir aber noch:
Wann muss $i und wann i schreiben? Warum enthält $? die Rückgabe des letzten Befehls, bzw. was bedeutet dieses Fragezeichen? Ich dachte, es würde für ein beliebiges Zeichen stehen.