Jak mogę tworzyć własne „polecenia powłoki” (np. Komenda mkdir / cd)?

41

Nie mogę powiedzieć, ile razy marzyłem o poleceniu, które zarówno utworzy katalog, jak i przejdzie do tego katalogu. Zasadniczo chciałbym ekwiwalent następujących rzeczy:

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

ale wystarczy wpisać /arbitrarily/long/pathraz, coś w stylu:

mk-cd /arbitrarily/long/path

Próbowałem utworzyć skrypt, aby to zrobić, ale zmienia tylko katalog w skrypcie. Chciałbym, aby katalog w powłoce również się zmienił.

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

Jak mogę sprawić, żeby to działało?

Christopher Bottoms
źródło
12
Dzięki cdod razu wybrałeś specjalną skrzynkę. : D
Daniel B
2
Nie do końca to, czego szukałem, ale super cdspokrewnione informacje (wróć do poprzedniego katalogu, używając cd -, używaj pushdi popdutrzymując „stos” katalogów): superuser.com/questions/324512/...
Christopher Bottoms
8
Możesz wpisać mkdir -p /very/long/path, następnie użyć cdspacji, a następnie naciśnij Alt +, .aby powtórzyć ostatni argument, tj. Nazwę katalogu.
choroba
2
Brak odpowiedzi, tylko komentarz podobny do @ choroba: Możesz także użyć mkdir -p /very/long/path; cd !#:2. Ciąg !#:2rozwinie się do argumentu nr. 2 (czyli trzeci argument /very/long/path, ponieważ liczenie zaczyna się od zera).
Ingo Blechschmidt
3
@IngoBlechschmidt, jeszcze łatwiejsze, ponieważ jest to ostatni argument ostatniego polecenia, którego możesz użyć !$. Cały czas używam tej konkretnej sztuczki, ale dzięki rozszerzeniu historii możesz zrobić znacznie więcej .
Wildcard

Odpowiedzi:

92

Najprostszym sposobem jest użycie funkcji powłoki:

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

Umieść go w swoim .bashrcpliku, aby był dostępny tak jak inne polecenie powłoki.

Powodem, dla którego nie działa jako skrypt zewnętrzny, jest cdzmiana bieżącego katalogu uruchomionego skryptu, ale nie wpływa na wywołujący. To jest z założenia! Każdy proces ma własny katalog roboczy, który jest dziedziczony przez jego dzieci, ale odwrotność nie jest możliwa.

O ile nie jest częścią potoku, działającą w tle lub jawnie w podpowłoce, funkcja powłoki nie działa w osobnym procesie, ale w tym samym procesie, tak jak w przypadku polecenia. Bieżącą powłokę katalogu można następnie zmienić za pomocą funkcji.

&&Tutaj stosowane do oddzielania zarówno polecenia stosowane oznacza, że jeśli pierwsze polecenie powiedzie ( mkdir) uruchomienia drugiej ( cd). W konsekwencji, jeśli mkdirnie uda się utworzyć żądanego katalogu, nie ma sensu próbować do niego wchodzić. Komunikat o błędzie jest drukowany przez mkdiri to wszystko.

-pOpcja wykorzystywane mkdirsą tam, aby powiedzieć to narzędzie do tworzenia wszelkiego brak katalogu, który jest częścią pełnej ścieżki nazwy katalogów przekazany jako argument. Jednym z efektów ubocznych jest to, że jeśli poprosisz o utworzenie już istniejącego katalogu, mkcdfunkcja nie zawiedzie i skończysz w tym katalogu. Można to uznać za problem lub funkcję. W pierwszym przypadku funkcję można zmodyfikować na przykład w taki sposób, aby po prostu ostrzegł użytkownika:

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

Bez tej -popcji zachowanie funkcji początkowej wyglądałoby zupełnie inaczej.

Jeśli katalog zawierający katalog do utworzenia nie istnieje, mkdiroznacza to awarię i funkcja.

Jeśli katalog, który ma zostać utworzony, już istnieje, mkdirrównież zawiedzie i cdnie zostanie wywołany.

Na koniec zauważ, że ustawienie / eksportowanie PWDjest bezcelowe, ponieważ powłoka już to robi wewnętrznie.

Edycja: Dodałem --opcję do obu poleceń dla funkcji, aby zezwolić na nazwę katalogu zaczynającą się od myślnika.

jlliagre
źródło
5
Należy również dodać, że to polecenie nie będzie dostępne na stałe, chyba że zostanie dodane do ~/.bashrcjednego z innych skryptów inicjalizacyjnych lub jednego z nich.
AFH,
Czy w bash nie ma sposobu, aby zrobić odpowiednik tcsh alias mk-cd 'mkdir -p \!:1; cd \!:1'bez funkcji? A może powinienem zacząć myśleć o funkcjach bash bardziej jak o rozszerzonych aliasach?
Thomas Padron-McCarthy
@ ThomasPadron-McCarthy Ten cshmechanizm przekazywania argumentów nie jest implementowany z aliasami typu bourne. Funkcje są rzeczywiście właściwą drogą, ponieważ są znacznie bardziej elastyczne.
jlliagre
@ ThomasPadron-McCarthy - Warto przyjrzeć się tej odpowiedzi , która zapewnia dość zawężający mechanizm dostępu do parametrów przekazywanych do aliasu (w definicji bar). Inną opcją byłoby użycie history 1, jak w alias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'- działa to dobrze dla przykładu pytającego, ale nie jest to ogólne rozwiązanie, ponieważ wszelkie inne polecenia w tym samym wierszu (np. Potokowanie wyjścia) spowodują jego awarię.
AFH,
3
@ ThomasPadron-McCarthy Powinieneś zacząć myśleć o aliasach tcsh bardziej jak ograniczonych funkcjach.
Gilles „SO- przestań być zły”
32

Chciałbym dodać alternatywne rozwiązanie.

Twój skrypt będzie działał, jeśli wywołasz go za pomocą polecenia . lub source:

. mk-cd /arbitrarily/long/path

Jeśli to zrobisz, exportskrypt nie będzie potrzebny. Możesz pominąć wpisywanie .za pomocą alias:

alias mk-cd='. mk-cd'

Powodem tego jest to, że skrypt normalnie działa w podpowłoce, więc środowisko jest tracone po zakończeniu, ale polecenie .(lub source) zmusza go do działania w bieżącej powłoce.

Ta technika może być przydatna w przypadku sekwencji poleceń, które są zbyt skomplikowane dla funkcji lub są w trakcie opracowywania i są często edytowane: po aliaswprowadzeniu .bash_aliasesmożna edytować skrypt w dowolnej chwili bez ponownej inicjalizacji.

Uwaga:

Jeśli napiszesz skrypt, który należy wywołać za pomocą polecenia ./ source, możesz to sprawdzić na dwa sposoby: -

  1. Z jakiegoś powodu skrypt wywołany z ./ sourcenie musi być wykonywalny, więc po prostu usuń to uprawnienie, a skrypt albo nie zostanie znaleziony w „$ PATH”, albo da błąd uprawnień, jeśli zostanie wywołany z jawną ścieżką.

  2. Alternatywnie, na początku skryptu można zastosować następującą sztuczkę:

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

Działa to, ponieważ w podpowłoce bindgeneruje ostrzeżenie, ale nie ma statusu błędu: dlatego readjest używany do dawania błędu przy braku danych wejściowych.

AFH
źródło
Dzięki za informacje na temat .. Robiłem . .bashrcniezliczoną ilość razy, ale nie wiedziałem, co dokładnie .robi. Czy to jest podobne do export?
cst1992
2
@ cst1992 - Nie, polecenia są zupełnie inne: export varkopiuje zmienną wewnętrzną vardo środowiska, gdzie jest dziedziczona i importowana jako zmienna w dowolnej podpowłoce, ale nie ma wpływu na powłokę nadrzędną. Zwykle tworzona jest podpowłoka w celu uruchomienia skryptu, a wszelkie wprowadzane przez nią zmiany (w tym zmienne eksportowane) są tracone po zakończeniu. Aby skrypt ustawiał zmienne w powłoce, musi on działać w tej powłoce, a to właśnie .robi polecenie: uruchom skrypt bez tworzenia podpowłoki. Co ciekawe, uruchamiane skrypty .nie muszą mieć uprawnień do wykonywania.
AFH
Dzięki za informację. Nalegam, abyś zamieścił te informacje w swoim poście. Jest to przydatne i szczerze mówiąc, komentarze nie mają gwarancji, że będą tam jutro.
cst1992
@ cst1992 - Odniosłem się do pierwotnego pytania, ale nie chcę zaśmiecać mojej odpowiedzi dodatkowymi problemami. Jeśli szukasz wyjaśnienia poleceń .i export, tytuł tego pytania nie skłoniłby cię do oczekiwania tutaj odpowiedzi, więc niech moja odpowiedź pozostanie bez zmian, ale dziękuję za komentarz.
AFH
+1 ogólnie, ale szczególnie dla aliasu. Mam kilka skryptów, które należy pobrać i zawsze zapominam je uruchamiać w ten sposób. Alias ​​ładnie się tym zajmuje.
Joe,
13

Nie jest to jedno polecenie, ale nie musisz ponownie wpisywać wszystkich ścieżek, których używasz !$(co jest ostatnim argumentem z poprzedniego polecenia w historii powłoki).

Przykład:

mkdir -p /arbitrarily/long/path
cd !$
RiaD
źródło
1

Tworząc aliasy . Bardzo popularny sposób tworzenia własnych poleceń.

Możesz utworzyć alias w locie:

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

Otóż ​​to. Jeśli chcesz zachować ten alias na stałe (nie tylko bieżącą sesję), umieść to polecenie w pliku kropkowym (zwykle ~ / .bash_aliases lub ~ / .bash_profile).

James
źródło
4
Ponieważ długa nazwa katalogu jest zakodowana na stałe, ten alias nie byłby bardzo przydatny, chyba że katalog ten jest regularnie niszczony przez inny proces.
jlliagre
1

Oprócz tego, co już zostało zasugerowane, chciałbym polecić kurwa . Jest to środowisko Python usprawniające proces powłoki przez naprawianie błędów. Zainspirowany tym tweetem , wszystko co musisz zrobić, to wpisać skonfigurowany alias (domyślnie fuck) i spróbujesz poprawić poprzednie polecenie. Jedna z jej poprawek zachowuje się następująco:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
Duncan X Simpson
źródło