Glory: Fragen zur Bash in Debian

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"

  1. 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

    1. Hellihello

      code lang=apache

      Warum benutzt du lang=apache?

      for dateipfad in ../Photobearbeitung/*.jpg
      do
      Dateiname=${dateipfad##*/}
      echo "Dateiname: $Dateiname \n"
      done

      vielleicht 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.

      1. 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"
        done

        vielleicht 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

        1. 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

          1. 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

  2. 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

    1. 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

    2. 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.