Schon 8 Jahre nach der Erstveröffentlichung gibt es eine neue Version von fcd, dem schnellen Verzeichniswechsler für die Shell.

(Anm.: Mithilfe der Kommentare habe ich einen Bug gefunden, und in den Kommentaren wird auch erklärt, wie man ihn beseitigt. Ein Update des Artikels folgt daher in Kürze).

Die neue Version kommt mit Leerzeichen in Verzeichnisnamen zurecht – eine Funktion, die ich nicht direkt selbst brauche, da ich keine Verzeichnisse mit Leerzeichen mache, aber immer mehr Programme legen solche Verzeichnisnamen an (z.B. ‚VirtualBox VMs‘, Zoom: ‚Unknown Organization‘, …) und diese tauchen störend in den Auswahllisten auf.

Der Fix war überraschend einfach, nachdem ich mich erst mal zu der v.a. kosmetisch gedachten Verbesserung aufgerafft hatte.

Hier der neue Code:

#!/bin/bash
#
# fcd.sh: Fast change directory.
#
# Source it from your .bashrc-file, to use it as a function.
#
# Update Ver.1.0, 2021-02-07
# changed from locate/egrep to mapfile, see code. Handles blanks in paths now flawlessly
# Update Ver.0.9, 2013-08-21, comments, publication
# Prototyp 2010
#
# Copyright GPLv3
# fcd () { # list=$(locate $1 | egrep "/$1$")
mapfile -d '' list < <(locate -b -0 -r "$1$") count=$(echo $list | wc -w ) case $count in 0) echo "unknown directory: "$1 && return # could search for partial matches Doc => Documentation ;; 1) if [[ -d "$list" ]]; then echo "1) match: $list" cd "$list" else echo "not a directory: $1" fi ;; *) select directory in "${list[@]}" "/exit/"; do if [[ "$directory" = "/exit/" ]]; then
break;
fi if [[ -d "$directory" ]]; then echo "multi) $directory" cd "$directory" break else echo "not a directory: "$1 fi done ;; esac }

18 Gedanken zu „fcd (fast-cd) Version 1.0

  1. m-bostaurus

    Hallo, das hört sich nach dem an, was ich schon lange suche. Allerdings schaffe ich es nicht, es laufen zu lassen. Muss ich den Code in die .bashrc eingeben. Oder die Datei fcd.sh ausführbar in ~./local/bin/ speichern?

    Antwort
    1. user unknown Autor

      Hi!
      Eine Option ist es, den Code in die .bashrc zu kopieren, dann ist es aus jeder geöffneten Shell nutzbar, ja.

      Unter dem obigen Link „Erstveröffentlichung“ habe ich einen etwas aufwendigeren Weg beschrieben, der einen in der Praxis selten relevanten Vorteil hat und einen kosmetischen.
      Er besteht darin, wie Du schreibst, es im Pfad als fcd.sh zu speichern, und ausführbar zu machen. Das ist mittlerweile wohl ~/.local/bin/, früher war da kein eigener Subordner im $HOME vorgesehen und so ist es bei mir schlicht ~/bin geworden und hat dann über zig Updates hinweg überlebt. Der Ordner muss nur im PATH sein, damit es gefunden wird. Aber nun kann man es nicht einfach mit `fcd.sh ORDNER` aufrufen, denn das Script erwartet kein Argument, um es an die Funktion weiterzureichen. Außerdem ist nur während das Script läuft das Verzeichnis gewechselt. Danach ist man wieder da, von wo man das Script aufgerufen hat – man muss es also als Funktion aufrufen.

      Dafür sourced man das Script aus der ~/.bashrc, d.h. es wird eingelesen und behandelt – ähnlich wie bei C-Programmen eine #include-Direktive – als stünde der Inhalt in der .bashrc selbst.
      Wozu wollte man es so umständlich machen:
      a) Aus kosmetischen Gründen, es hält die .bashrc kleiner. Sollte es Probleme geben (was ich nicht erwarte) muss man nicht das Script auskommentieren oder sichern, sondern könnte das source-Kommando auskommentieren.
      b) In manchen Fällen wird die .bashrc nicht gelesen, zum Beispiel von anderen Scripten. Ob man da die Funktionalität haben will, ist eher fraglich, aber man könnte dann auch dort das Script sourcen. Wie es sich bei einem login per ssh verhält, weiß ich auch nicht aus dem Kopf, aber auch da kann man das Script dann einfach sourcen.

      Sourcen geht so, dass man mit

      source ~/local/bin/fcd.sh 
      # oder
      . ~/local/bin/fcd.sh 
      

      das Einlesen des Scripts bewerkstelligt. Das Sourcen klappt auch interaktiv in der Shell, wenn man es erst testen will, bevor man es in der .bashrc verewigt.
      Wer das Sourcen noch nicht kennt, für den ist die Syntax mit dem isolierten Punkt sicher gewöhnungsbedürftig. Der macht aber das gleiche, wie das Source-Kommando.

      Bestimmt heißt es auch nicht, wie Du geschrieben hast, ~./local/bin/ mit Punkt nach der Tilde, sondern, wenn, dann ~/.local/bin/ . Bestimmt ein Typo.

      Antwort
      1. m-bostaurus

        Danke für die ausführliche Antwort. Durch sie habe ich es besser verstanden, doch leider funktioniert es bei mir nicht.
        Ich arbeite mit 5.11.0-1-MANJARO.
        Der Ordner steht in meinem $PATH: echo $PATH
        /home/user/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
        Im erstgenannten Ordner liegt fcd.sh; in der .bashrc habe ich die source Anweisung untergebracht.
        Folgende Reaktion erscheint im Terminal:
        fcd home
        bash: locate: Kommando nicht gefunden.
        unknown directory: home

        Wenn ich mich nach /home/user/.local/bin bewege und zwei Befehlsvarianten versuche, sieht es so aus:
        ./fcd.sh home –Es erfolgt keine Reaktion …
        fcd home
        bash: locate: Kommando nicht gefunden.
        unknown directory: home

        Vielleicht hast Du oder ein anderer Leser eine Idee, weshalb es nicht funktioniert.

        Antwort
        1. m-bostaurus

          Hi, … also … Arch kennt den Befehl locate nicht. Ich versuche stattdessen find zu nehmen, was tendenziell der richtige Weg ist. Doch find -b wiederum gibt es nicht, wird mir in einer Reaktion gesagt. Das bedeutet für mich, dass ich nach Familieneinsatz herausfinden werde, was locate -b bedeutet und was das Pendant dazu bei find ist. Es wird, es wird …

          Antwort
          1. m-bostaurus

            Uff … alles gut. Ich habe zuerst mlocate installiert, dann sudo updatedb laufen lassen. Schließlich habe ich dann fcd home eingegeben. Und: Es funktioniert.

            Antwort
          2. user unknown Autor

            Schön weiter unten zu lesen, dass Du Dir selbst helfen konntest. Aiuergflks Kommentar, der den gleichen Weg empfohlen hat, lag noch in der Moderationsqueue – da es sein erster Kommentar war, musste ich ihn erst freischalten.

            Man könnte auch find verwenden, aber das durchsucht das ganze Dateisystem (wenn man es nicht eingrenzt) und das dauert i.d.R. länger – der Vorteil wäre, dass man auch ganz neue Dateien so findet. Es dauert aber, wenn man noch alte Festplatten hat, im Minutenbereich.

            Wenn man immer nur in einem Teilast wie /home sucht wird es sicher schneller. Da es freie Software ist, kannst Du ja mal damit rumexperimentieren.

            Statt locate würde man `find / -type d -name „$1“ 2>/dev/null` verwenden. Mit „-type d“ schränkt man von vorneherein auf Directories ein, und -name sucht nach genauen Matches, wenn man keine Wildcards im Suchwort verwendet. Man kann aber, wenn man nach neuen Verzeichnissen sucht, auch manuell erst `sudo updatedb` aufrufen.

            Es gibt wohl immer mehr Desktopuser, die die Shell nur äußerst selten benutzen, und für die hat man wohl oft darauf verzichtet, updatedb/locate per default zu installieren. Jahrzehntelang war deren Vorhandensein quasi Standard.

            Antwort
  2. m-bostaurus

    „Es gibt wohl immer mehr Desktopuser, die die Shell nur äußerst selten benutzen, …“ Bei mir ist es gerade umgekehrt und hängt damit zusammen, dass ich (coronaunabhängig) Zeit habe, um mich da stärker reinzuhängen.
    Das Arch-Paket, das mir weiterhalf, habe ich zuerst an der falschen Stelle gesucht. Ubuntu liefert die Datenbank immer schon mit; Manjaro KDE offensichtlich nicht.
    Ich habe in der Datei /etc/updatedb.conf Ordner und Unterordner von /media aus der Auflistung nicht zu beachtender Ordner herausgenommen, da ich sehr viele Verzeichnisse und Dateien in diesem Bereich speichere. fcd oder die Datenbank gibt jedoch nur Suchergebnisse für die Ebene /media/ebene1 heraus, aber Ordner in /media/ebene1/ebene2 werden nicht gefunden (drücke ich mich verständlich aus?).
    Auch fiel mir auf bei der Suche nach bin, dass mir /bin als einziges gezeigt wird, aber z. Bsp. /home/user/.locate/bin nicht und auch /usr/bin nicht.
    Ob das am Script oder an dem, was die Datenbank sucht und speichert, liegt, kann ich nicht beurteilen.
    Den Vorschlag mit find werde ich im Lauf des Vormittags versuchen und dann auch eine Rückmeldung geben.
    Vielen Dank an User unknown und aiuergflk!

    Antwort
    1. m-bostaurus

      Mit find klappt es nicht. Genauer geschrieben:
      Der Befehl „find / -type d -name „08-mozart“ 2>/dev/null“ findet alle Speicherorte dieses Ordners, selbst die eine, die ich schon längst vergessen habe. Es dauert natürlich. Aber führe ich das Script aus, erhalte ich als Rückmeldung: „unknown directory: 08-mozart“. Die Integration in .bashrc habe ich bei leicht geändertem Namen (ffcd.sh) analog zu fcd.sh gemacht.
      Allerdings ist auch find in seinem Verhalten für mich nicht ganz durchschaubar. Der Befehl „find /media/ -type d -name „alma-centos“ 2>/dev/null“ findet nichts; der Befehl „find /media/vb4/ -type d -name „alma-centos“ 2>/dev/null“ findet den Ordner.

      Antwort
      1. user unknown Autor

        Das ist wahrscheinlich ein WordPressproblem, aber mir wird beide Male `alma-centos` in typografischen Anführungsstrichen angezeigt. Bei einem Namen wie diesem brauchst Du überhaupt keine. Vielleicht hilft es, die Fehlermeldungsunterdrückung (2>/dev/null) rauszunehmen. Wenn man mit find das Wurzelverzeichnis, also alles durchsucht, bekommt man irrsinnig viele Fehlermeldungen, dass das Verzeichnis xy nur mit root-Rechten durchsucht werden darf. Das brauchst Du in /media vielleicht nicht.

        Antwort
      2. user unknown Autor

        Wollte man find im Script benutzen, dann würde man schreiben:

        list=($(find $HOME -type d -name "$1" 2>/dev/null))
        # statt der Zeile mit mapfile ..., und dann weiter wie in der letzten Korrektur:
        count=${#list[@]}

        Das ist aber noch nicht auf Festigkeit gegen Leerzeichen in Pfadnamen geprüft – im Gegenteil, es funktioniert sicher nicht mit Leerzeichen im Pfad.

        Also wenn Du damit experimentierst, entweder nur in Strukturen, die keine Leerzeichen haben, oder Du musst mit find/xargs/mapfile selbst eine Lösung basteln.

        Da ich ohnehin nicht auf find zurückzugreifen plane – weil es zu langsam ist – suche ich da aber nicht selbst nach einer Lösung. Wenn Du bei der Suche nicht weiterkommst, kannst Du ja gerne auf ubuntuusers.de fragen, oder auf Stackechange.

        Antwort
    2. user unknown Autor

      Das sind interessante Beobachtungen.
      Das mit bin wollte ich nicht recht glauben, habe es bei mir selbst ausprobiert und zu meiner Überraschung festgestellt, dass mir auch nur ein bin-Ordner gezeigt wird.

      Also habe ich mal das locate-Kommando, ohne die -0, alleine eingegeben und festgestellt, dass es über 1500 Treffer ausspuckt. Daher ist meine erste Vermutung, dass es Beschränkungen bei der Arraygröße gibt, die hier verletzt werden.

      locate -b -r "bin$" | wc -l
      1532
      # dagegen
      locate -b -r "^bin$" | wc -l
      80

      Das werde ich mal als erstes untersuchen. Ein Suchstring „bin“ sucht alles, was die Zeichenkette „bin“ enthält, findet daher auch „bind“, „robin“ und „Kabine“. Das Dollar am Ende steht für Wortende und würde „bind“ und „Kabine“ aus der Ergebnisliste ausschließen, das Caret vor bin („^bin“) steht für „Wortanfang“ und wirft auch „robin“ raus.

      Das Problem mit Ebene1/2 habe ich wohl verstanden, aber kann es natürlich schlecht nachvollziehen. Der Einsatz von find ist auch nicht einfach eine Kommandoersetzung in einer Zeile. Das zu erklären muss ich auch etwas ausholen, aber primär muss ich jetzt erst mal einkaufen und ich habe auch noch etwas bezahlte Arbeit hier rumliegen.

      Ad hoc kannst Du folgende Änderung machen, um das bin-Problem zu fixen:

      fcd ()
      {
      mapfile -d '' list < <(locate -b -0 -r "^$1$")
      count=${#list[@]}

      Statt die Zahl der Treffer mit wc zu ermitteln, was mit 0-terminierten-Strings nicht funktioniert, hätte ich das gleich mit der Arraygröße (`${#list[@]}`) lösen sollen.

      Vielleicht wirkt sich das ja auch bei dem ebene1/2-Problem aus?

      Antwort
  3. m-bostaurus

    Super. Diese Variante ist eine wirkliche Verbesserung. Mir wurden 553 bin gemeldet, auch welche, die tief gestaffelt in /media/ liegen. Seltsam: der oben erwähnte Ordner „alma-centos“ wird nicht gefunden; sein Elternordner „vb4“ sehr wohl.
    Ansonsten will ich Dir mit meinem Interesse an dem Script keinen Stress machen. Ich bin einfach hoch erfreut, weil ich das „Landen“ im richtigen Ordner im Terminal umständlich finde. Und da bist dann Du, der sagt, das könne man auch einfacher haben.

    Antwort
    1. user unknown Autor

      Danke, aber Bugrerports – so nervig sie sein können – sind ja nur deswegen nervig, weil man alle Fehler beseitigen möchte. Und die Fehler nicht zur Kenntnis zu nehmen, das ist ja wohl keine Alternative. Dann soll man den Code für sich behalten und nicht veröffentlichen.

      Als Entwickler muss man ein schizophrenes Verhältnis zu Fehlerreports haben: Man hasst sie, weil sie offenbaren, dass man selbst nicht fehlerlos ist, aber andererseits ersparen sie einem ja die Arbeit, selbst mühevoll danach zu suchen. Viele Fehler findet man aufgrund weißer Flecken der eigenen Landkarte nicht. Daher muss man auch dankbar sein für jeden, der das Tool nicht mit der Bemerkung „noch so ein Schrott“ in die Tonne kickt, sondern den Fehler meldet, den er gefunden hat. Das ist ja auch Arbeit, kostet Zeit und Überwindung.

      Heute frage ich mich, wieso ich noch nie in einen bin-Ordner mit meinem Programm gewechselt bin, und das Problem selbst gefunden habe. Vielleicht, weil verschiedene Javatools, von denen ich einige nutze, fleißig bin-Ordner überall anlegen, und eine Auswahlliste mit 80 Einträgen auch relativ unübersichtlich ist.

      Antwort
      1. m-bostaurus

        Mann-o-Mann; das Rätsel um meinen „Lieblingsordner“ alma-centos ist gelöst, und es lag an mir. Der Ordner liegt auf einer Partition, die ich dauerhaft unter /mnt gemountet habe.
        Die Datei /etc/updatedb.conf enthält eine Zeile, die mit PRUNEPATH beginnt. In ihr wird festgelegt, wo nicht gesucht wird für die Erstellung der Datenbank. In der steht regulär /media (das hatte ich schon geändert) und eben auch /mnt. Nach dem Löschen dieser Angabe, dem Speichern und einem nachfolgenden sudo updatedb wird der Ordner zuverlässig gefunden.
        Auf S. 47 in LinuxWelt 1/2021 fand ich übrigens einen Hinweis für diejenigen, die auch USB-Laufwerke scannen wollen. In der genannten Datei gibt es auch eine Zeile, beginnend mit PRUNEFS. In dieser muss man usbfs löschen. Danach werden auch USB-Laufwerke für die Erstellung der Datenbank gescannt.
        @user unknown: Ich freue mich immer, wenn jemand Comic Sans als Schriftart verwendet wie ich, wobei ich der allmählich untreu zugunsten von Comic Neue werde …
        … ich würde gern auf Dein Script bei den Internetseiten https://forum.manjaro.org/, https://www.manjaro-forum.de/ und https://de.manjaro.org/ aufmerksam machen, wenn Du mir ein Signal sendest, dass Du einverstanden bist …

        Antwort
  4. user unknown Autor

    Ja, ich bin sehr einverstanden damit, dass Du es veröffentlichst, insbes. wenn mit Link.

    Ich wollte die aktualisierte Version diese Woche noch mit einem Blogartikel würdigen, damit Leser des ubuntuusers.de-Planeten die Änderungen mitbekommen. Ich denke dazu sollte ich dann auch noch ein, zwei Sätze zu updatedb als Kommentar in den Quellcode schreiben.

    Ich habe auf diesen Kommentar schon mal geantwortet, aber über so ein Kommentar-Popup von WordPress. Offenbar ist diese Antwort verloren gegangen. So erinnere ich mich auch noch, wg. Comic-Sans nachgefragt zu haben. Ich setze die ja vornehmlich in Cartoons ein, wofür sie ja auch gedacht ist. Hast Du sie sonst wo gesehen?

    Antwort

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.