Wie kann ich meine eigenen "Shell-Befehle" erstellen (zB mkdir / cd combo)?

41

Ich kann nicht sagen, wie oft ich mir einen Befehl gewünscht habe, der sowohl ein Verzeichnis erstellt als auch in dieses Verzeichnis wechselt. Grundsätzlich hätte ich gerne das Äquivalent von:

mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path

aber nur einmal eingeben zu müssen /arbitrarily/long/path, so etwas wie:

mk-cd /arbitrarily/long/path

Ich habe versucht, ein Skript dafür zu erstellen, aber es ändert nur das Verzeichnis innerhalb des Skripts. Ich möchte, dass sich auch das Verzeichnis in der Shell geändert hat.

#!/bin/bash
mkdir $1
cd $1
export PWD=$PWD

Wie könnte ich das schaffen?

Christopher Bottoms
quelle
12
Mit cdhaben Sie von Anfang an einen Sonderfall ausgewählt. : D
Daniel B
2
Nicht genau das, wonach ich gesucht habe, aber super coole cdInformationen (kehren Sie zum vorherigen Verzeichnis zurück cd -und verwenden Sie pushdund popd, um einen "Stapel" von Verzeichnissen zu verwalten): superuser.com/questions/324512/…
Christopher Bottoms
8
Sie können ein Leerzeichen eingeben mkdir -p /very/long/pathund dann verwenden cdund dann Alt + drücken ., um das letzte Argument, dh den Namen des Verzeichnisses, zu wiederholen.
Choroba
2
Keine Antwort, nur ein Kommentar ähnlich dem von @ choroba: Du kannst auch verwenden mkdir -p /very/long/path; cd !#:2. Die Zeichenfolge !#:2wird zu Argument nr erweitert. 2 (das dritte Argument /very/long/path, da die Zählung mit Null beginnt).
Ingo Blechschmidt
3
@IngoBlechschmidt, noch einfacher, da es das letzte Argument des letzten Befehls ist, können Sie einfach verwenden !$. Ich benutze diesen besonderen Trick die ganze Zeit, obwohl es viel mehr gibt, was Sie mit der Erweiterung der Geschichte tun können .
Wildcard

Antworten:

92

Der einfachste Weg ist die Verwendung einer Shell-Funktion:

mkcd() {
    mkdir -p -- "$1" && cd -- "$1"
}

Fügen Sie es in Ihre .bashrcDatei ein, damit es Ihnen wie ein anderer Shell-Befehl zur Verfügung steht.

Der Grund, warum es nicht als externes Skript funktioniert, liegt darin cd, dass das aktuelle Verzeichnis des ausgeführten Skripts geändert wird, das aufrufende jedoch nicht betroffen ist. Dies ist beabsichtigt! Jeder Prozess verfügt über ein eigenes Arbeitsverzeichnis, das von seinen untergeordneten Prozessen geerbt wird. Das Gegenteil ist jedoch nicht möglich.

Sofern ein Teil einer Pipeline nicht im Hintergrund oder explizit in einer Subshell ausgeführt wird, wird eine Shell-Funktion nicht in einem separaten Prozess, sondern in demselben Prozess ausgeführt, genau wie wenn der Befehl aus einer Quelle stammt. Die aktuelle Verzeichnis-Shell kann dann durch eine Funktion geändert werden.

Das &&hier zum Trennen der beiden Befehle verwendete bedeutet, wenn der erste Befehl erfolgreich ist ( mkdir), führen Sie den zweiten aus ( cd). Wenn mkdirdaher das angeforderte Verzeichnis nicht erstellt werden kann, ist es sinnlos, darauf zuzugreifen. Eine Fehlermeldung wird von gedruckt mkdirund das wars.

Die mit verwendete -pOption mkdirweist dieses Dienstprogramm an, ein fehlendes Verzeichnis zu erstellen, das Teil des vollständigen Pfads des als Argument übergebenen Verzeichnisnamens ist. Ein Nebeneffekt ist, dass, wenn Sie ein bereits vorhandenes Verzeichnis erstellen möchten, die mkcdFunktion nicht fehlschlägt und Sie in diesem Verzeichnis landen. Dies kann als Problem oder Merkmal angesehen werden. Im ersteren Fall kann die Funktion beispielsweise so geändert werden, dass der Benutzer lediglich gewarnt wird:

mkcd() {
    if [ -d "$1" ]; then
        printf "mkcd: warning, \"%s\" already exists\n" "$1"
    else
        mkdir -p "$1" 
    fi && cd "$1"
}

Ohne die -pOption wäre das Verhalten der Ausgangsfunktion sehr unterschiedlich gewesen.

Wenn das Verzeichnis mit dem zu erstellenden Verzeichnis noch nicht vorhanden ist, mkdirschlägt die Funktion fehl.

Wenn das zu erstellende Verzeichnis bereits vorhanden ist, mkdirschlägt dies ebenfalls fehl und cdwird nicht aufgerufen.

Beachten Sie schließlich, dass das Setzen / Exportieren PWDsinnlos ist, da die Shell dies bereits intern tut.

Bearbeiten: Ich habe die --Option zu beiden Befehlen für die Funktion hinzugefügt , damit ein Verzeichnisname mit einem Bindestrich beginnt.

jlliagre
quelle
5
Sie sollten auch hinzufügen, dass dieser Befehl nur dauerhaft verfügbar ist, wenn er zu ~/.bashrceinem der anderen Initialisierungsskripten hinzugefügt wird .
AFH
Gibt es in bash keine Möglichkeit, das Äquivalent von tcshs alias mk-cd 'mkdir -p \!:1; cd \!:1'ohne Funktion auszuführen ? Oder sollte ich mir Bash-Funktionen eher als erweiterte Aliase vorstellen?
Thomas Padron-McCarthy
@ ThomasPadron-McCarthy Dieser cshÜbergabemechanismus für Argumente ist nicht mit Aliasen im Bournestil implementiert. Funktionen sind in der Tat der richtige Weg, da sie viel flexibler sind.
Juli
@ ThomasPadron-McCarthy - Vielleicht möchten Sie sich diese Antwort ansehen , die einen ziemlich unübersichtlichen Mechanismus für den Zugriff auf Parameter bietet, die an einen Alias ​​(in der Definition von bar) übergeben wurden. Eine andere Möglichkeit wäre history 1, wie in alias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'- zu verwenden. Dies funktioniert für das Beispiel des Fragestellers gut, ist jedoch keine allgemeine Lösung, da alle anderen Befehle in derselben Zeile (z. B. das Weiterleiten der Ausgabe) dazu führen, dass sie fehlschlagen.
AFH
3
@ ThomasPadron-McCarthy Sie sollten sich die Aliase von tcsh eher als eingeschränkte Funktionen vorstellen.
Gilles 'SO- hör auf böse zu sein'
32

Ich möchte eine alternative Lösung hinzufügen.

Ihr Skript wird funktionieren , wenn Sie es mit dem aufrufen . oder sourceBefehl ein :

. mk-cd /arbitrarily/long/path

Wenn Sie dies tun, ist das exportim Skript nicht erforderlich . Sie können die Eingabe .von umgehen, indem Sie Folgendes verwenden alias:

alias mk-cd='. mk-cd'

Der Grund, warum dies funktioniert, ist, dass ein Skript normalerweise in einer Sub-Shell ausgeführt wird, sodass seine Umgebung nach Abschluss verloren geht. Der Befehl .(oder source) erzwingt jedoch, dass es in der aktuellen Shell ausgeführt wird.

Diese Technik kann für Kommandosequenzen nützlich sein , die für eine Funktion zu komplex sind oder die sich in der Entwicklung und werden häufig bearbeitet: einmal die aliasin eingegeben wurde .bash_aliases, können Sie das Skript nach Belieben ohne Neuinitialisierung bearbeiten können.

Beachte das Folgende:-

Wenn Sie ein Skript schreiben, das mit dem Befehl / aufgerufen werden muss , gibt es zwei Möglichkeiten, dies sicherzustellen:.source

  1. Aus irgendeinem Grund muss ein Skript, das mit ./ aufgerufen wird, sourcenicht ausführbar sein. Entfernen Sie daher einfach diese Berechtigung, und das Skript wird entweder nicht in "$ PATH" gefunden oder gibt einen Berechtigungsfehler aus, wenn es mit einem expliziten Pfad aufgerufen wird.

  2. Alternativ kann der folgende Trick am Kopf des Skripts verwendet werden:

bind |& read && { echo 'Must be run from "." or "source" command'; exit 1; }

Dies funktioniert, weil in einer Sub-Shell bindeine Warnung ausgegeben wird, obwohl kein Fehlerstatus vorliegt: wird daher readverwendet, um bei keiner Eingabe einen Fehler zu melden.

AFH
quelle
Danke für die Info auf .. Ich habe . .bashrcunzählige Male gemacht, wusste aber nicht genau was das .macht. Ist es ähnlich zu export?
cst1992
2
@ cst1992 - Nein, die Befehle sind völlig anders: export varDie interne Variable wird varin die Umgebung kopiert , wo sie geerbt und als Variable in eine beliebige Sub-Shell importiert wird. Dies hat jedoch keine Auswirkungen auf eine übergeordnete Shell. Normalerweise wird eine Sub-Shell erstellt, um ein Skript auszuführen, und alle Änderungen (einschließlich exportierter Variablen) gehen verloren, wenn sie abgeschlossen sind. Damit ein Skript Variablen in einer Shell setzen kann, muss es in dieser Shell ausgeführt werden. Der .Befehl führt das Skript aus, ohne eine Sub-Shell zu erstellen. Seltsamerweise .benötigen Skripte, die von ausgeführt werden, keine ausführbare Berechtigung.
AFH
Danke für die Information. Ich würde darauf bestehen, dass Sie diese Informationen in Ihren Beitrag aufnehmen. Dies ist nützlich, und ehrlich gesagt können Kommentare nicht garantieren, dass sie morgen da sind.
cst1992
@ cst1992 - Ich habe die ursprüngliche Frage beantwortet, möchte meine Antwort jedoch nicht mit Hilfsproblemen überfrachten. Wenn Sie nach einer Erklärung der Befehle .und suchen export, wird der Titel dieser Frage Sie nicht dazu bringen, hier eine Antwort zu erwarten. Daher werde ich meine Antwort so lassen, wie sie ist, aber danke für Ihren Kommentar.
AFH
+1 im Allgemeinen, aber vor allem für den Alias. Ich habe ein paar Skripte, die beschafft werden müssen, und vergesse immer, sie so auszuführen. Der Alias ​​kümmert sich gut darum.
Joe
13

Kein einziger Befehl, aber Sie müssen nicht den gesamten verwendeten Pfad erneut eingeben !$(dies ist das letzte Argument des vorherigen Befehls im Shell-Verlauf).

Beispiel:

mkdir -p /arbitrarily/long/path
cd !$
RiaD
quelle
1

Indem Sie Aliase erstellen . Sehr beliebte Möglichkeit, eigene Befehle zu erstellen.

Sie können einen Alias ​​im Handumdrehen erstellen:

alias myalias='mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path'

Das ist es. Wenn Sie diesen Alias ​​dauerhaft beibehalten möchten (nicht nur die aktuelle Sitzung), fügen Sie diesen Befehl in eine Punktdatei ein (normalerweise ~ / .bash_aliases oder ~ / .bash_profile).

James
quelle
4
Da der lange Verzeichnisname fest codiert ist, wäre dieser Alias ​​nur dann sehr nützlich, wenn dieses Verzeichnis regelmäßig von einem anderen Prozess zerstört wird.
Juli
1

Zusätzlich zu dem, was bereits vorgeschlagen wurde, möchte ich thefuck weiterempfehlen . Es ist ein Python-Framework, mit dem Sie Ihren Shell-Prozess optimieren können, indem Sie Fehler beheben. Inspiriert von diesem Tweet , müssen Sie nur Ihren konfigurierten Alias ​​(standardmäßig fuck) eingeben und es wird versucht, Ihren vorherigen Befehl zu korrigieren. Eine seiner Korrekturen verhält sich wie folgt:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
Duncan X Simpson
quelle