Dlaczego „cd” nie działa w skrypcie powłoki?


766

Próbuję napisać mały skrypt, aby zmienić bieżący katalog na katalog projektu:

#!/bin/bash
cd /home/tree/projects/java

Zapisałem ten plik jako proj, dodałem uprawnienia do wykonywania chmodi skopiowałem go do /usr/bin. Kiedy nazywam to: projnic nie robi. Co ja robię źle?



W przyszłości zawsze możesz spróbować go przetestować pwdna ostatniej linii. Więc przed zakończeniem skryptu możesz sprawdzić, czy działa, czy nie ...
sobi3ch

5
@lesmana jak to jest duplikat?
i

@aland Ponieważ PO robi nie w rzeczywistości uruchomić skrypt, to dlaczego reż pracy nie zmienia się dla niego. cdpolecenie działa dobrze w skryptach, spróbuj sam.
Alexander Gonchiy,

Odpowiedzi:


634

Skrypty powłoki są uruchamiane w podpowłoce, a każda podpowłoka ma własną koncepcję tego, czym jest bieżący katalog. cdPowiedzie, ale jak najszybciej wyjść podpowłoki, jesteś z powrotem w interaktywnej powłoki i nigdy nic się nie zmieniło.

Jednym ze sposobów obejścia tego problemu jest użycie aliasu:

alias proj="cd /home/tree/projects/java"

7
Aliasy nie są tak elastyczne w zarządzaniu ani zmianie. W przypadku wielu płyt CD skrypty mogą być lepsze.
Thevs

16
Funkcje są bardziej elastyczne niż aliasy, więc właśnie tam będziesz szukać, gdy aliasy nie wystarczą.
ephemient

4
Czy warto zauważyć, że w MS-DOS zachowanie skryptów polegało na tym, że wywoływany skrypt może zmienić katalog (a nawet dysk) wywołującej powłoki poleceń? A czy Unix nie ma tej wady?
Jonathan Leffler,

23
Jonathan: chociaż to prawda, to nie jest tak naprawdę związane z pytaniem. Odpowiedzi na temat SO byłyby dwa razy dłuższe, gdyby musiały na każdej liście znaleźć odpowiednie braki w MS-DOS!
Greg Hewgill

17
Innym sposobem obejścia tego jest zebranie pliku skryptu: . my-scriptlub source my-script.
HelloGoodbye,

503

Nie robisz nic złego! Zmieniłeś katalog, ale tylko w podpowłoce, która uruchamia skrypt.

Możesz uruchomić skrypt w bieżącym procesie za pomocą polecenia „kropka”:

. proj

Ale wolałbym sugestię Grega, by w tym prostym przypadku użyć aliasu.


95
.jest również pisane source, wybierz cokolwiek, co uważasz za bardziej niezapomniane.
ephemient

47
@Ephemient: Dobry punkt źródłowy To wyjaśnia, dlaczego to działa źródło Udowadnia również, że lenistwo, a nie konieczność, jest często matką źródła wynalazków
Adam Liss

8
@ephemient: zauważ, że źródło jest używane w powłoce C i Bash; nie jest obsługiwany w powłokach POSIX i Korn ani w klasycznej powłoce Bourne'a.
Jonathan Leffler

2
„źródło” jest używane w powłoce Z
Nathan Moos

1
@AdamLiss Działa świetnie, ale ma jedną wadę - w jakiś sposób polecenie source / dot wyłącza możliwość automatycznego uzupełniania nazwy skryptu / polecenia za pomocą klawisza TAB. Nadal można dodawać argumenty / dane wejściowe za pomocą klawisza TAB, ale niestety nazwa polecenia musi zostać wprowadzona bezpośrednio. Czy wiesz, jak sprawić, aby klawisz TAB działał w tym przypadku?
Rafał

215

W cdskrypcie technicznie działało, ponieważ zmieniło katalog powłoki, która go uruchomiła, ale był to proces oddzielny od powłoki interaktywnej.

Kompatybilnym z Posix sposobem rozwiązania tego problemu jest zdefiniowanie procedury powłoki zamiast skryptu poleceń wywoływanego przez powłokę .

jhome () {
  cd /home/tree/projects/java
}

Możesz to po prostu wpisać lub umieścić w jednym z różnych plików startowych powłoki.


6
Zgadzam się, to najlepsza odpowiedź, wpadła na siebie. Również alias może być odpowiedni w niektórych sytuacjach, ale jeśli próbuje użyć cd w skrypcie i chce dodać do niego cokolwiek innego, alias byłby bezużyteczny.
Justin Buser

4
To wygląda na rozwiązanie! Próbuję to zaimplementować, ale nie działa :( oto mój skrypt: #! / Bin / bash jhome () {echo "proszę" cd / home echo "praca"} jhome
Gant Laborde

1
@GantMan, powinieneś dodać to w „plikach startowych powłoki”, takich jak ~ / .bashrc
Jackie Yeh

3
Możesz także użyć argumentu (lub dwóch ...), tj .:jhome(){ cd /home/tree/projects/$1; }
irbanana

1
Powinno to być właściwe, ponieważ umożliwia użycie znaków specjalnych, na przykład „-”.
bangkokguy

159

cdOdbywa się wewnątrz skorupy skryptu. Po zakończeniu skryptu powłoka kończy działanie, a następnie zostajesz w katalogu, w którym byłeś. „Źródło” skryptu, nie uruchamiaj go. Zamiast:

./myscript.sh

zrobić

. ./myscript.sh

(Zwróć uwagę na kropkę i spację przed nazwą skryptu).


2
To jest fajne i prawdopodobnie dobrze wiedzieć. Co dokładnie robi to drugie (jak to działa)? Jest to prawdopodobnie najlepsze rozwiązanie w tym wątku.
Jonasz

2
fwiw, w sekcji komentarzy w odpowiedzi Adama Lissa, efemeryczny odpowiada na pytanie, co „.” jest. To to samo, cosource
g19fanatic

1
Bądź ostrożny, „źródło” to bzdura. tj. dash (domyślnie Debian dla / bin / sh) nie obsługuje go, podczas gdy „.” działa zgodnie z oczekiwaniami.
allo

Świetna odpowiedź! także jeśli myscript.shznajduje się w katalogu zawartym w $ PATH, możesz go pobrać z dowolnego miejsca bez podawania pełnej ścieżki.
CodeBrew,

To działało dla mnie najlepiej. To rozwiązanie jest najprostsze (+1).
HuserB1989

96

Aby utworzyć skrypt bash, który będzie cd do wybranego katalogu:

Utwórz plik skryptu

#! / bin / sh
# file: / scripts / cdjava
#
cd / home / askgelal / projects / java

Następnie utwórz alias w pliku startowym.

#! / bin / sh
# plik /scripts/mastercode.sh
#
alias cdjava = '. / scripts / cdjava ”

  • Utworzyłem plik startowy, w którym zrzucam wszystkie moje aliasy i funkcje niestandardowe.
  • Następnie źródła tego pliku do mojego .bashrc, aby ustawić go przy każdym uruchomieniu.

Na przykład utwórz główny plik aliasów / funkcji: /scripts/mastercode.sh
(Umieść alias w tym pliku).

Następnie na końcu pliku .bashrc :

źródło /scripts/mastercode.sh



Teraz łatwo jest cd do katalogu java, wystarczy wpisać cdjava i już tam jesteś.


2
plik mastercode.shnie potrzebuje shabang ( #!/bin/sh), ponieważ nie jest (i nie może być) wykonany w podpowłoce. Ale jednocześnie musisz udokumentować „smak” powłoki tego pliku; np. ksh lub bash (lub (t) csh / zsh, itp.) i prawie na pewno nie jest tak naprawdę sh. Zazwyczaj dodaję komentarz (ale nie shebang), aby to przekazać; np. „ten plik ma być pozyskiwany (z bash), a nie uruchamiany jako skrypt powłoki”.
Michael

Kolejna wskazówka (dla bash): jeśli używasz zmiennych w skrypcie, to skoro skrypt jest wykonywany sourced (przez alias), zmienne te wyciekną do twojego środowiska powłoki. Aby tego uniknąć, wykonaj całą pracę skryptu w funkcji i po prostu wywołaj go na końcu skryptu. W ramach funkcji zadeklaruj dowolne zmienne local.
Rhubbarb

w Ubuntu po prostu stwórz ~ / .bash_aliases (jeśli jeszcze nie istnieje). Następnie po prostu dodaj tam swoje aliasy, uruchom ponownie terminal i gotowe.
Vlad

51

Możesz użyć .do wykonania skryptu w bieżącym środowisku powłoki:

. script_name

lub alternatywnie, jego bardziej czytelny, ale specyficzny dla powłoki alias source:

source script_name

Pozwala to uniknąć podpowłoki i pozwala, aby wszelkie zmienne lub wbudowane (w tym cd) wpływały na bieżącą powłokę.


38

Pomysł Jeremy'ego Ruten'a na użycie dowiązania symbolicznego wywołał myśl, która nie przeszła żadnej innej odpowiedzi. Posługiwać się:

CDPATH=:$HOME/projects

Wiodący dwukropek jest ważny; oznacza to, że jeśli w bieżącym katalogu znajduje się katalog „dir”, wówczas „ cd dir” zmieni się na to, zamiast skakać gdzie indziej. Przy ustawionej wartości, jak pokazano, możesz:

cd java

a jeśli w bieżącym katalogu nie ma podkatalogu o nazwie java, to zabierze Cię bezpośrednio do $ HOME / projects / java - bez aliasów, bez skryptów, bez wątpliwych poleceń ani poleceń dot.

Mój $ HOME to / Users / jleffler; mój $ CDPATH to:

:/Users/jleffler:/Users/jleffler/mail:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work

30

Użyj exec bashna końcu

Skrypt bash działa w bieżącym środowisku lub w środowisku potomnym, ale nigdy w środowisku macierzystym.

Jednak to pytanie jest często zadawane, ponieważ chce się pozostawić (nowy) monit bash w pewnym katalogu po wykonaniu skryptu bash z innego katalogu.

W takim przypadku po prostu uruchom podrzędną instancję bash na końcu skryptu:

#!/usr/bin/env bash
cd /home/tree/projects/java
exec bash

16
To tworzy nową podpowłokę. Po wpisaniu exitpowrócisz do powłoki, w której uruchomiłeś ten skrypt.
tripleee

1
Kiedy skrypt był wywoływany kilka razy, wydaje się, że tworzy zagnieżdżone powłoki i muszę wiele razy pisać „exit”. Czy można to jakoś rozwiązać?
VladSavitsky,

Zobacz inne odpowiedzi: użyj aliasu, użyj „pushd / popd / dirs” lub użyj „source”
qneill

25

Mam kod do pracy za pomocą. <your file name>

./<your file name> dawka nie działa, ponieważ nie zmienia katalogu w terminalu, po prostu zmienia katalog specyficzny dla tego skryptu.

Oto mój program

#!/bin/bash 
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace

Oto mój terminal

nova:~ Kael$ 
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$ 

2
Jaka jest różnica między . somethingi ./something?? Ta odpowiedź zadziałała dla mnie i nie rozumiem dlaczego.
Dracorat

. somethingpozwala na uruchomienie skryptu z dowolnej lokalizacji, ./somethingwymaga przebywania w katalogu, w którym plik jest przechowywany.
Projjol


Czy umieścisz kropkę w linii shebang? #!. /bin/bash?
Sigfried


12

Po uruchomieniu skryptu powłoki jest uruchamiane nowe wystąpienie tej powłoki ( /bin/bash). W ten sposób skrypt po prostu odpala powłokę, zmienia katalog i kończy działanie. Innymi słowy, cd(i inne takie polecenia) w skrypcie powłoki nie wpływają ani nie mają dostępu do powłoki, z której zostały uruchomione.


11

W moim konkretnym przypadku potrzebowałem zbyt wiele razy, aby zmienić ten sam katalog. Więc na moim .bashrc (używam ubuntu) dodałem

1 -

$ nano ~. / bashrc

 function switchp
 {
    cd /home/tree/projects/$1
 }

2-

$ source ~ / .bashrc

3 -

$ switchp java

Bezpośrednio zrobi to: cd / home / tree / projects / java

Mam nadzieję, że to pomaga!


1
@WM Możesz ostatecznie zrobić „$ switchp” bez żadnego parametru, aby przejść bezpośrednio do drzewa projektu
workdreamer

2
@workdreamer Opisane powyżej podejście do funkcji rozwiązało tutaj mały problem z przechodzeniem do podfolderów, które mają ten sam folder nadrzędny w moim systemie. Dziękuję Ci.
WM

10

Możesz wykonać następujące czynności:

#!/bin/bash
cd /your/project/directory
# start another shell and replacing the current
exec /bin/bash

EDYCJA: Można to również „kropkować”, aby zapobiec tworzeniu kolejnych powłok.

Przykład:

. ./previous_script  (with or without the first line)

1
To staje się dość niechlujne po kilku uruchomieniach. Będziesz musiał exit(lub ctrl + d) kilka razy opuścić powłokę, na przykład .. Alias ​​jest o wiele bardziej przejrzysty (nawet jeśli polecenie powłoki wyświetla katalog, a cd do wyniku - alias coś = "cd getnewdirectory.sh")
dbr

Uwaga „exec”. Powoduje zastąpienie starej powłoki.
Thevs

2
Exec zastępuje tylko podpowłokę, która uruchomiła polecenie cd, a nie powłokę, która uruchomiła skrypt. Gdybyś skropił skrypt, byłbyś poprawny.
Jonathan Leffler,

Niech będzie „kropkowany” - nie ma problemu. Właśnie zaproponowałem rozwiązanie. To nie zależy od tego, jak to uruchomisz.
Thevs

2
Hehe, myślę, że lepiej jest po prostu „kropkować” skrypt
jednowierszowy

7

Zmienia tylko katalog samego skryptu, podczas gdy bieżący katalog pozostaje taki sam.

Zamiast tego możesz użyć łącza symbolicznego . Umożliwia utworzenie „skrótu” do pliku lub katalogu, więc wystarczy wpisać tylko coś takiego cd my-project.


Posiadanie tego dowiązania symbolicznego w każdym katalogu byłoby uciążliwe. Możliwe byłoby umieszczenie dowiązania symbolicznego w $ HOME, a następnie wykonanie polecenia „cd ~ / my-project”. Szczerze mówiąc, korzystanie z CDPATH jest prostsze.
Jonathan Leffler,

7

Możesz łączyć pseudonimy i kropki Adama i Grega, aby stworzyć coś bardziej dynamicznego -

alias project=". project"

Teraz uruchomiony alias projektu wykona skrypt projektu w bieżącej powłoce, w przeciwieństwie do podpowłoki.


Właśnie tego potrzebowałem, aby utrzymać cały mój skrypt i po prostu wywołać go z bash i utrzymać bieżącą sesję - to jest DOSKONAŁA odpowiedź.
Matt The Ninja,

7

Możesz połączyć alias i skrypt,

alias proj="cd \`/usr/bin/proj !*\`"

pod warunkiem, że skrypt echa ścieżki docelowej. Zauważ, że są to backty otaczające nazwę skryptu. 

Na przykład skrypt może być

#!/bin/bash
echo /home/askgelal/projects/java/$1

Zaletą tej techniki jest to, że skrypt może przyjmować dowolną liczbę parametrów wiersza poleceń i emitować różne miejsca docelowe obliczone na podstawie możliwie złożonej logiki.


3
dlaczego? możesz po prostu użyć: proj() { cd "/home/user/projects/java/$1"; }=> proj "foo"(lub, proj "foo bar"<= jeśli masz spacje) ... lub nawet (na przykład): proj() { cd "/home/user/projects/java/$1"; shift; for d; do cd "$d"; done; }=> proj a b c=> robi cdw/home/user/projects/java/a/b/c
Michael


5

W twoim pliku ~ / .bash_profile. dodaj następną funkcję

move_me() {
    cd ~/path/to/dest
}

Zrestartuj terminal i możesz pisać

move_me 

i zostaniesz przeniesiony do folderu docelowego.


3

Możesz użyć operatora &&:

cd myDirectory && ls


Downvote: To nie próbuje odpowiedzieć na faktyczne pytanie tutaj. Możesz uruchomić dwa polecenia w wierszu polecenia ( &&jeśli chcesz, aby drugi był zależny od pierwszego, albo po prostu za pomocą ;lub nowego wiersza między nimi), ale umieszczenie tego w skrypcie spowoduje powrót do „dlaczego powłoka nadrzędna nie używa nowy katalog, kiedy cdfaktycznie się powiódł? ”
tripleee

3

Podczas gdy pozyskiwanie skryptu, który chcesz uruchomić, jest jednym z rozwiązań, powinieneś być świadomy, że ten skrypt może następnie bezpośrednio modyfikować środowisko twojej bieżącej powłoki. Ponadto nie można już przekazywać argumentów.

Innym sposobem jest zaimplementowanie skryptu jako funkcji w bash.

function cdbm() {
  cd whereever_you_want_to_go
  echo "Arguments to the functions were $1, $2, ..."
}

Ta technika jest używana przez autojump: http://github.com/joelthelion/autojump/wiki, aby zapewnić ci naukę zakładek do katalogu powłoki.


3

Możesz utworzyć funkcję podobną do poniższej w swoim, .bash_profilektóra będzie działać płynnie.

Poniższa funkcja przyjmuje opcjonalny parametr, którym jest projekt. Na przykład możesz po prostu uruchomić

cdproj

lub

cdproj project_name

Oto definicja funkcji.

cdproj(){
    dir=/Users/yourname/projects
    if [ "$1" ]; then
      cd "${dir}/${1}"
    else
      cd "${dir}"
    fi
}

Nie zapomnij o swoim źródle .bash_profile


1
znacznie lepsza odpowiedź niż alias
Pax0r

To rozwiązanie jest genialne w swojej prostocie i elastyczności.
Kjartan

3

To powinno zrobić, co chcesz. Przejdź do katalogu zainteresowań (z poziomu skryptu), a następnie spawnuj nową powłokę bash.

#!/bin/bash

# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash

Jeśli uruchomisz to, zabierze Cię do interesującego katalogu, a gdy go opuścisz, przeniesie Cię z powrotem do pierwotnego miejsca.

root@intel-corei7-64:~# ./mov_dir.sh

root@intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root@intel-corei7-64:~#

Spowoduje to nawet powrót do oryginalnego katalogu po wyjściu ( CTRL+ d)


3
Odrodzenie nowego bashu oznacza utratę historii i zacznie się od nowa.
Tisch

2

SPOJRZENIE Po pewnym czasie, ale zrobiłem następujące:

utwórz plik o nazwie case

wklej następujące pliki:

#!/bin/sh

cd /home/"$1"

zapisz to, a następnie:

chmod +x case

Utworzyłem również alias w moim .bashrc:

alias disk='cd /home/; . case'

teraz kiedy piszę:

case 12345

zasadniczo piszę:

cd /home/12345

Możesz wpisać dowolny folder po „case”:

case 12

case 15

case 17

co jest jak pisanie:

cd /home/12

cd /home/15

cd /home/17

odpowiednio

W moim przypadku ścieżka jest znacznie dłuższa - ci goście podsumowali ją wcześniej ~ info.


1
cdW aliasu jest zbędny i nieeleganckie; alias powinien po prostu „. Zamiast tego ~ / case`. Również casejest zarezerwowanym słów kluczowych, więc to raczej kiepski wybór dla nazwiska.
tripleee

1

Nie potrzebujesz skryptu, ustaw tylko poprawną opcję i utwórz zmienną środowiskową.

shopt -s cdable_vars

W twojej ~/.bashrcpozwala cdna zawartość zmiennych środowiskowych.

Utwórz taką zmienną środowiskową:

export myjava="/home/tree/projects/java"

i możesz użyć:

cd myjava

Inne alternatywy .


0

Jeśli używasz ryb jako skorupy, najlepszym rozwiązaniem jest utworzenie funkcji. Jako przykład, biorąc pod uwagę oryginalne pytanie, możesz skopiować 4 poniższe wiersze i wkleić je do wiersza polecenia ryby:

function proj
   cd /home/tree/projects/java
end
funcsave proj

Spowoduje to utworzenie funkcji i zapisanie jej do późniejszego użycia. Jeśli Twój projekt się zmieni, po prostu powtórz proces, używając nowej ścieżki.

Jeśli wolisz, możesz ręcznie dodać plik funkcji, wykonując następujące czynności:

nano ~/.config/fish/functions/proj.fish

i wprowadź tekst:

function proj
   cd /home/tree/projects/java
end

i na koniec naciśnij ctrl + x, aby wyjść, a następnie y powróć, aby zapisać zmiany.

( UWAGA: pierwsza metoda użycia funcsave tworzy dla Ciebie plik proj.fish ).


0

Mam prosty skrypt bash o nazwie p, aby zarządzać zmianą katalogu na
github.com/godzilla/bash-stuff,
po prostu włóż skrypt do lokalnego katalogu bin (/ usr / local / bin)
i umieść

alias p='. p'

w twoim .bashrc


co to robi wygląda na to, że uruchomiłby się rekurencyjnie, choć jestem pewien, że uruchomiłeś go wcześniej, bo nie;)
Ryan Taylor


0

To stare pytanie, ale jestem naprawdę zaskoczony, że nie widzę tutaj tej sztuczki

Zamiast używać cd możesz użyć

export PWD=the/path/you/want

Nie trzeba tworzyć podpowłoki ani używać aliasów.

Pamiętaj, że Twoim obowiązkiem jest upewnić się, że / path / you / want istnieje.


Niestety nie zadziałało.
ttfreeman

0

Jak wyjaśniono w innych odpowiedziach, zmieniłeś katalog, ale tylko w podpowłoce, która uruchamia skrypt . nie wpływa to na powłokę nadrzędną.

Jednym z rozwiązań jest użycie funkcji bash zamiast skryptu bash ( sh); poprzez umieszczenie kodu skryptu bash w funkcji. Dzięki temu funkcja jest dostępna jako polecenie, a następnie zostanie wykonana bez procesu potomnego, a zatem każde cdpolecenie wpłynie na powłokę wywołującą.

Funkcje Bash:

Jedną z funkcji profilu bash jest przechowywanie niestandardowych funkcji, które można uruchamiać w terminalu lub w skryptach bash w taki sam sposób, jak uruchamiasz aplikację / polecenia, może to również służyć jako skrót do długich poleceń.

Aby twoja funkcja była wydajna, musisz skopiować ją na końcu kilku plików

/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile

Możesz sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profileszybko edytować / tworzyć te pliki

Jak :

Skopiuj kod skryptu bash do nowej funkcji na końcu pliku profilu bash i zrestartuj terminal, możesz następnie uruchomić cdddowolną funkcję, którą napisałeś.

Przykład skryptu

Tworzenie skrótu do za cd ..pomocącdd

cdd() {
  cd ..
}

skrót

ll() {
  ls -l -h
}

skrót

lll() {
  ls -l -h -a
}

-2

Możesz wykonać niektóre linie w tej samej podpowłoce, jeśli zakończysz linie odwrotnym ukośnikiem.

cd somedir; \
pwd
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.