Das Aufrufen einer Bash-Funktion funktioniert nicht wie es sollte, um zum letzten Verzeichnis zu wechseln.

563
snapchatdotcom

Ich erwarte also, dass sich meine Funktion in ein kürzlich geändertes Verzeichnis ändert.

Das ist es:

function cdrc { echo 'cd "$(ls -t | HEAD -1)"'; } 

Wenn ich zu einem neuen Verzeichnis wechseln möchte:

$~ cd Desktop/Folder $~ cdrc 

Ich bekomme:

-Bash: Desktop: Befehl nicht gefunden

-2

2 Antworten auf die Frage

1
Kamil Maciorowski

Warum funktioniert Ihre Funktion nicht?

Dafür gibt es wenige Gründe:

  • Ich schätze, echoein Artefakt wird zum Testen der Syntax verwendet. Es macht keinen Sinn, wenn Sie möchten, dass die Funktion das tut, was Sie beschrieben haben.
  • Versalien HEAD. In Linux sollte es sein head. Ich bin mir nicht sicher über andere OS-es, wo Sie ausführen können, bashund head. HEADkann oder kann nicht in einigen von ihnen arbeiten, headsollte aber überall funktionieren.
  • Parsing lswird nicht empfohlen. Es gibt einen Artikel darüber . Der Hauptpunkt in Ihrem Fall ist, dass lsNamen mit Sonderzeichen oder nicht druckbaren Zeichen nicht zuverlässig gedruckt werden können.
  • Es gibt keine Logik, um nur Verzeichnisse zu testen. Möglicherweise versuchen Sie cd, eine Datei zu suchen, wenn kein Verzeichnis vorhanden ist.
  • Es gibt keine Logik, um die Situation zu handhaben, wenn das aktuelle Arbeitsverzeichnis leer ist.

Alle diese Probleme können mit Ausnahme dieser Parsing- lsSache debuggt werden . Es ist ein Designfehler. Wenn Sie glauben, dass lsEinschränkungen Sie nicht beissen, können Sie eine Lösung aus dieser anderen Antwort wählen .

Um einige Tests durchzuführen, können Sie ein problematisches Verzeichnis mit erstellen mkdir "$(echo -ne "foo\nbar")"; ls-basierte Lösungen werden wahrscheinlich fehlschlagen, wenn dies das Verzeichnis cdrcsollte cd. So entfernen Sie das problematische Verzeichnis rmdir "$(echo -ne "foo\nbar")".

Ich habe es geschafft, eine sicherere Funktion zu schaffen.


Lösung

function cdrc { cd "$(find -maxdepth 1 -mindepth 1 -type d -exec stat --printf "%Y %n\0" {} + | sort -znr | head -zn 1 | cut -f 2- -d " ")" ;} 

Erläuterung

Um meine Funktion zu erklären, werde ich sie klarer schreiben. Beachten Sie a \am Ende einer Zeile, dass bashder Befehl in der nächsten Zeile fortgesetzt wird. daher wird mein Code wie ein Einzeiler behandelt, er kann als Ganzes in interaktiv eingefügt werden bash.

function cdrc { \ cd "$( \ find -maxdepth 1 -mindepth 1 -type d -exec \ stat --printf "%Y %n\0" {} + | sort -znr | head -zn 1 | cut -f 2- -d " " \ )" \ ;} 

Das Verfahren ist wie folgt:

  • Zunächst findwird ausgeführt. Es geht nicht in die Unterverzeichnisse ( -maxdepth 1), es findet auch nicht das aktuelle Verzeichnis ( -mindepth 1). Es findet nur Verzeichnisse ( -type d). Dann wird der statBefehl ausgeführt (Danke an -exec):
    • statGibt die Zeit der letzten Datenänderung ( %Y, mtime, Sekunden seit Epoche), ein Leerzeichen und den Namen ( %n) aus. Aufgrund der --printfOption wird das Zeilenvorschubzeichen nicht hinzugefügt, sondern \0als Nullzeichen interpretiert, das am Ende jeder Zeile eingefügt werden sollte.
    • {}ist ein Teil der find -execSyntax. Während der findAusführung wird es durch den Verzeichnisnamen ersetzt, so statdass das Ziel bekannt ist.
    • +ist auch ein Teil der find -execSyntax. Es bewirkt find, dass mehrere Namen an Single übergeben werden stat(und statdamit umgehen können). Auf diese Weise werden weniger statProzesse erstellt, das ist schneller.

In diesem Moment haben wir keine oder mehr Zeilen. Sie sehen ähnlich aus:

1493488341 directory name 1497365306 troublesome?directory name 

Sie sind jedoch nullterminiert, selbst wenn Namen mit lästigen Zeichen vorhanden sind, werden sie ordnungsgemäß behandelt. In der ersten Spalte gibt es mtimes ohne führende Leerzeichen (ich habe das statVerhalten mit Nummern verschiedener Länge geprüft, um sicherzugehen, dass sich das erste Leerzeichen von mtime und dem Verzeichnisnamen unterscheidet.

  • Diese Ausgabe wird weiter verarbeitet:
    • sortsortiert Zeilen nach numerischem Wert ( -n), verwendet umgekehrte Reihenfolge ( -r) und arbeitet mit nullterminierten Zeichenfolgen ( -z). Auf diese Weise steht das Verzeichnis, das wir brauchen, jetzt in der ersten Zeile.
    • Dann headbleibt nur die erste Zeile ( -n 1); Es wird auch empfohlen, mit nullterminierten Strings ( -z) zu arbeiten.
    • cutschneidet die Linie, behandelt den Raum als Trennzeichen ( -d " ") und verlässt das zweite Feld und alles, was folgt ( -f 2-), dh alles nach dem ersten Raum Es funktioniert mit nullterminierten Strings ( -z). Die endgültige Ausgabe ist der gewünschte Verzeichnisname.

Beachten Sie, dass die Ausgabe leer ist, wenn sich im aktuellen Arbeitsverzeichnis kein Verzeichnis befindet.

  • $(…)wird ersetzt durch die Ausgabe von allem, was sich darin befindet. In diesem Moment haben wir entweder cd "some directory name"oder cd "". Der vorherige Befehl macht, was Sie wollen. Letzteres (wenn es kein Verzeichnis gibt) tut nichts.

Die Funktion schlägt fehl, wenn das Verzeichnis, in dem es sich befinden soll, cdverschoben oder umbenannt wird, nachdem findes gefunden wurde. Auch statkönnen Fehler (n) aus, wenn ein beliebiges Verzeichnis ist (wieder) verschoben / umbenannt, wenn die Funktion arbeitet.

0
tharrrk

Wenn Sie alle Verzeichnisse aufnehmen möchten, dh mit einem Punkt beginnen

function cdrc { cd "$(ls -1atp|grep -v '^\.\.\?/$'|grep '/$'|head -1)"; } 

Ansonsten viel einfacher

function cdrc { cd "$(ls -1tp|grep '/$'|head -1)"; }