Czy istnieje jedna linijka, która pozwala mi utworzyć katalog i przenieść się do niego w tym samym czasie?

150

Powtarzam wiele:

mkdir longtitleproject
cd longtitleproject

Czy można to zrobić w jednym wierszu bez powtarzania nazwy katalogu? Jestem tutaj na haju.

methodofaction
źródło
2
Powiązane (ale bardziej ogólne): Uruchom dwie komendy na jednym argumencie (bez skryptów) .
G-Man,
1
mkdir longtitleprojectnastępniecd !^
user262826,
Został również omówiony na askubuntu.com/q/927463/295286 i askubuntu.com/q/249314/295286
Sergiy Kolodyazhnyy

Odpowiedzi:

160

Nie ma wbudowanego polecenia, ale możesz łatwo napisać funkcję, która wywołuje mkdirwtedy cd:

mkcd () {
  mkdir "$1"
  cd "$1"
}

Umieść ten kod w swoim ~/.bashrcpliku (lub ~/.kshrcdla użytkowników ksh lub ~/.zshrcdla użytkowników zsh). Definiuje funkcję o nazwie mkcd. "$1"zostanie zastąpiony argumentem funkcji po uruchomieniu.

Ta prosta wersja ma kilka wad:

  • Nie można jednocześnie utworzyć łańcucha podkatalogów. Poprawka: przekaż -popcję do mkdir. (Może to być lub nie być pożądane, ponieważ zwiększa ryzyko, że literówka mkcd mydierctory/newsubpozostanie niewykryta, np. Z przyjemnością utworzy mydierctoryi mydierctory/newsubkiedy zamierzasz utworzyć newsubwewnątrz istniejącej mydirectory).
  • Jeśli argument zaczyna się od, -ale nie tylko -, wtedy mkdiri cdzinterpretuje go jako opcję. Jeśli tak -, to cdzinterpretuje to w znaczeniu $OLDPWD. Jeśli po nim +następuje 0 lub więcej cyfr, to cdw zsh zinterpretuje to jako indeks na stosie katalogów. Możesz rozwiązać pierwszy problem, ale nie pozostałe dwa, przekazując --przed argumentem. Możesz rozwiązać wszystkie te problemy, przygotowując ./argument, jeśli jest to ścieżka względna.
  • mkdirnie następuje CDPATH, ale cdtak jest, więc jeśli ustawisz CDPATHwartość, która nie zaczyna się od .(co prawda nieco nietypowa konfiguracja), cdmożesz przenieść cię do innego katalogu niż właśnie utworzony. Przygotowanie ./do ścieżek względnych naprawia to (powoduje CDPATHzignorowanie).
  • Jeśli się mkdirnie powiedzie, próbuje się wykonać cd. Poprawka: użyj, &&aby rozdzielić dwa polecenia.

Wciąż dość proste:

mkcd () {
  case "$1" in /*) :;; *) set -- "./$1";; esac
  mkdir -p "$1" && cd "$1"
}

Ta wersja wciąż ma potencjał, aby cdprzejść do innego katalogu niż ten, który mkdirwłaśnie utworzono w jednym przypadku krawędzi: jeśli argument mkcdzawiera ..i przechodzi przez dowiązanie symboliczne. Na przykład, jeśli bieżący katalog jest /tmp/herei mylinkjest dowiązaniem symbolicznym do /somewhere/else, wówczas mkdir mylink/../footworzy, /somewhere/else/foopodczas gdy cd mylink/../foozmienia się w foo. Nie wystarczy szukać dowiązań symbolicznych w argumencie, ponieważ powłoka śledzi również dowiązania symboliczne w swoim bieżącym katalogu, więc cd /tmp/mylink; mkdir ../foo; cd ../foonie zmienia się w nowy katalog ( /somewhere/else/foo), ale w /tmp/foo. Rozwiązaniem tego cdproblemu jest zezwolenie wbudowanemu na rozpoznanie wszystkich ..komponentów ścieżki w pierwszej kolejności (nie ma sensu używać foo/..iffoonie istnieje, więc mkdirnigdy nie trzeba go widzieć ..).

Dochodzimy do tej solidnej, choć nieco krwawej wersji:

mkcd () {
  case "$1" in
    */..|*/../) cd -- "$1";; # that doesn't make any sense unless the directory already exists
    /*/../*) (cd "${1%/../*}/.." && mkdir -p "./${1##*/../}") && cd -- "$1";;
    /*) mkdir -p "$1" && cd "$1";;
    */../*) (cd "./${1%/../*}/.." && mkdir -p "./${1##*/../}") && cd "./$1";;
    ../*) (cd .. && mkdir -p "${1#.}") && cd "$1";;
    *) mkdir -p "./$1" && cd "./$1";;
  esac
}

(Ćwiczenie: dlaczego używam podpowłoki do pierwszego cdpołączenia?)

Jeśli mkdir zawiedzie, chcę mieć pewność, że nie zmieniłem bieżącego katalogu. Cofanie się za pomocą cd - lub $ OLDPWD nie jest wystarczająco dobre, jeśli powłoka nie ma uprawnień do przejścia do bieżącego katalogu. Również wywołanie cd aktualizuje OLDPWD, więc chcemy to zrobić tylko raz (lub przywrócić OLDPWD).


Istnieją również mniej wyspecjalizowane sposoby, aby nie musieć wpisywać słowa z poprzedniego wiersza:

  • Wpisz cd , a następnie Esc .(lub Alt+ .), aby wstawić ostatni argument z poprzedniego polecenia.
  • cd !$wykonuje cdsię na ostatnim argumencie poprzedniego polecenia.
  • Naciśnij, Upaby przywołać poprzednią linię poleceń, a następnie edytuj ją, aby zmienić mkdirna cd.
Gilles
źródło
Dzięki! Esc. wydaje mi się najwygodniejszy dla mnie, czy sekwencja klawiszy ma jakieś specjalne znaczenie?
methodofaction
Jest to po prostu sekwencja Bash (i odziedziczona z ksh, a także działa w zsh) dla „powtórz ostatnie słowo poprzedniego polecenia”. Używam tego dość często.
geekozaur
30
@Gilles Zaczynam myśleć, że konto „Gilles” jest w rzeczywistości wspólne dla panelu ekspertów. ;-)
Keith
@StephaneChazelas /a/b/..//faktycznie działałoby, ale nie działało /a/b/../c. Naprawiony. Zadałem to pytanie szerszej publiczności.
Gilles
1
mkcd() { mkdir -p "$1" && cd "$1"; }nie wydaje się być problemem w (moim przypadku) zsh. mkdir -p /var/tmp/somewhere/else /tmp/here; cd /tmp/here; ln -s /var/tmp/somewhere/else mylink; mkdir -p mylink/../foo && cd mylink/../foo; pwd(obejmuje konfigurację i) wyświetla, /tmp/here/fooco zostało utworzone (i czego się spodziewałem). bashbłędnie tworzy i zmienia się w /var/tmp/somewhere/foo.
Adam Katz
133

Jest to jedna linijka, której potrzebujesz. Nie jest wymagana żadna inna konfiguracja:

mkdir longtitleproject && cd $_

$_Zmienna, w bash, jest ostatnim argumentem podane do poprzedniego polecenia. W takim przypadku nazwa właśnie utworzonego katalogu. Jak wyjaśniono w man bash:

_         At  shell  startup,  set to the absolute pathname used to invoke
          the shell or shell script being executed as passed in the  envi
          ronment  or  argument  list.   Subsequently, expands to the last
          argument to the previous command, after expansion.  Also set  to
          the  full  pathname  used  to  invoke  each command executed and
          placed in the environment exported to that command.  When check
          ing  mail,  this  parameter holds the name of the mail file cur
          rently being checked."$_" is the last argument of the previous command.

Służy cd $_do pobierania ostatniego argumentu poprzedniego polecenia zamiast, cd !$ponieważ cd !$podaje ostatni argument poprzedniego polecenia w historii powłoki :

cd ~/
mkdir folder && cd !$

kończysz w domu (lub ~ /)

cd ~/
mkdir newfolder && cd $_

kończysz w nowym folderze pod domem !! (lub ~ / newfolder)

Jesús Carrera
źródło
23
Dlaczego, u
licha,
1
@JSmyth Zgadzam się, to jest jeden-liner, który korzysta z funkcjonalności natywnej powłoki
sming
Myślę, że OP stara się unikać używania dwóch poleceń. Ta odpowiedź jest (prawie) tak samo ważna jak robienie mkdir foo && cd foo, co nie jest przydatne.
josemigallas,
7
OP prosi o linijkę, która nie wymaga powtarzania nazwy katalogu, i to jest to
Jesús Carrera
Jest to tradycyjny jednolinijka, o której mówisz lub widziałeś, że inni używają jej w dokumentach / tutorialach. Istnieją tutaj 3 idealne odpowiedzi. Ten, autorstwa Jordana-Harrisa i wybraną odpowiedź. Zależy od konfiguracji i preferencji.
Wade
30

Nigdy nie przyszło mi do głowy, aby opisać to zachowanie, ponieważ wpisuję następujące informacje prawie co godzinę ...

$ mkdir someDirectory<ENTER>
$ cd !$

gdzie bash uprzejmie zastępuje !$ostatnie słowo ostatniego wiersza; tzn. podana długa nazwa katalogu.

Ponadto uzupełnianie nazw plików jest Twoim przyjacielem w takich sytuacjach. Jeśli twój nowy katalog był jedynym plikiem w tym folderze, szybki podwójny plik TABda ci nowy katalog bez ponownego wchodzenia do niego.

Chociaż fajnie jest, że bash pozwala na tworzenie skryptów tak typowych zadań, jak inne odpowiedzi sugerują, myślę, że lepiej jest nauczyć się funkcji edycji wiersza poleceń, które bash ma do zaoferowania, aby podczas pracy na innym komputerze nie brakowało składni cukier dostarczany przez niestandardowe skrypty.

Obrabować
źródło
1
Czy jest dobre miejsce, aby dowiedzieć się o najważniejszych dziwactwach typu bash, takich jak ten?
dominicbri7,
@ dominicbri7 Zasadniczo znajduje się w „edycji wiersza poleceń bash” Googling sam daje następujące wyniki jako najlepszy wynik web.mit.edu/gnu/doc/html/features_7.html Mówiąc dokładniej ,! $ jest przykładem programu Word Designator patrz gnu. org / software / bash / manual / bashref.html # Word-Designators
Rob
17

Zgodnie z Jakimi dostosowaniami dokonałeś się w swoim profilu powłoki, aby zwiększyć wydajność? , tak to robię:

# make a directory and cd to it
mcd()
{
    test -d "$1" || mkdir "$1" && cd "$1"
}

oznacza to, że działa również, jeśli katalog już istnieje.

Mikel
źródło
4
-pOpcja mkdir będzie tłumić błędów.
glenn jackman
1
mcd to już istniejące polecenie. Chociaż właśnie podałeś przykład, użyłem go sam, ponieważ jest to list krótszy niż mkcd.
Dharmit
@Dharmit Shah: Jakie jest istniejące mcdpolecenie? Który pakiet lub projekt udostępnia to polecenie?
Mikel
2
mtools udostępnia polecenie mcd. Na stronie podręcznika napisano: „Polecenie mcd służy do zmiany katalogu roboczego mtools na dysku MS-DOS”.
Dharmit
13

Jeśli używasz Oh My Zsh, istnieje polecenie o nazwie take, które właśnie to robi. Wyglądałoby to mniej więcej tak.

take myfolder

Znalazłem to przypadkiem. Właśnie spojrzałem i jest wymieniony na tym cheatheat z wiki Oh My Zsh GitHub. Jest to dość przydatne polecenie i najwyraźniej bardzo łatwe do samodzielnego stworzenia.

Jordan Harris
źródło
1
Nigdy nie wiedziałem o take:) Idealnie! btw - iTerm2 z Oh My Zsh. Istnieją tutaj 3 idealne odpowiedzi. Ten, autorstwa @ jesús-carrera i wybraną odpowiedź. Zależy od konfiguracji i preferencji.
Wade
3

Lub możesz po prostu stworzyć krótką zmienną w locie i użyć jej dwukrotnie x = longproject ; mkdir $x ; cd $x- co, przyznaję, jest jeszcze dłuższe niż użycie funkcji shellscript :)

sakisk
źródło
0

Oto niewielki wariant, który warto wspomnieć:

function mkdir() {
    local dir=$1
    command mkdir "$dir"  &&  cd "$dir"
}

Dodaj to do swojego, ~/.bash_profilea będziesz mógł używać mkdirjak zwykle (kiedy już to zrobisz source), tyle że teraz uruchomi funkcję powyżej zamiast standardowego mkdirpolecenia.

Zauważ, że to nie sprawdza poprawności danych wejściowych zgodnie z zaakceptowaną odpowiedzią Gillesa , ale pokazuje, jak możesz (skutecznie) zastąpić wbudowane.

Z dokumentów (nieco parafrazując):

command mkdir [args]działa mkdirz argsignorowaniem dowolnej funkcji powłoki o nazwie mkdir. Wykonywane są tylko wbudowane polecenia powłoki lub polecenia znalezione podczas przeszukiwania ŚCIEŻKI. Jeśli jest nazwana funkcja powłoki ls, uruchomienie command lsw obrębie funkcji spowoduje wykonanie polecenia zewnętrznego lszamiast wywoływania funkcji rekurencyjnie

Wierzę, że builtinosiąga podobny wynik command.

Arj
źródło
zdecydowanie powinieneś zacytować$dir
Jeff Schaller
@Jeff, zgadzam się, ale zaakceptowana odpowiedź ma całą potrzebną weryfikację. Przedstawiam użycie commandjako alternatywy.
Arj
0

Dodanie funkcji pomocniczej do BASH, ZSH lub KSH

Utwórz mkcdpolecenie dla swojego środowiska w jednym wierszu

echo -e 'mkcd() {\n mkdir -p "$1" && cd $_\n}' >> ~/.${0//-/}rc && . ~/.${0//-/}rc
Proximo
źródło
-1

Właśnie zautomatyzowałem powyższe odpowiedzi i utworzyłem skrypt wykonywalny jednorazowo:

fun='
mkcd ()
{
    mkdir -p -- "$1" && cd -P -- "$1"
}'

echo "$fun" >> ~/.bashrc

Po prostu skopiuj to do nowego pliku mkcd.shi uruchom tylko raz w terminalu przez bash mkcd.sh. Następnie uruchom, source ~/.bashrcaby działał w bieżącej sesji.

Następnie możesz użyć mkcd some_dirdo utworzenia i przejścia bezpośrednio do tego katalogu.

subtleseeker
źródło
Sugerujesz napisanie skryptu (w pliku), którego jedynym celem jest dołączenie do ~/.bashrcpliku (z udzieloną już odpowiedzią)? A jak sugerujesz utworzenie tego mkcd.shskryptu? Może z redaktorem? To wygląda na więcej pracy niż tylko edycję ~/.bashrc. Jakie to ma przewagę nad przyjętą odpowiedzią? ……………………………………………………………………… PS To się nie powiedzie z powodu cytowania problemów, które mówią mi, że nawet nie próbowałeś tego.
Scott
Przykro mi to mówić, ale tak, jak napisałem w odpowiedzi, użyłem powyższych odpowiedzi. To działa. Jeśli mi nie wierzysz, spróbuj. I o tym, że nie próbowałem, wykorzystałem cały dzień, aby napisać dzisiaj takie skrypty w bash i python.
subtleseeker
Próbowałem tego, co napisałeś. To nie działa
Scott