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?

Ashokgelal
źródło
10
duplikat krzyżowy: superuser.com/questions/176783/…
lesmana
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"
Greg Hewgill
źródło
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.

Adam Liss
źródło
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.

DigitalRoss
źródło
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).

Tzachi.e
źródło
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ś.

Matt Thomas
źródło
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ę.

Sagar
źródło
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
Jonathan Leffler
źródło
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
Serge Stroobandt
źródło
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$ 
kaelhop
źródło
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
20

po prostu uruchom:

cd /home/xxx/yyy && command_you_want
warhansen
źródło
To zadziałało dla mnie dzięki @warhansen
Ashish Kumar
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.

Daniel Spiewak
źródło
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!

pracujący
źródło
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)
Thevs
źródło
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.

Jeremy Ruten
źródło
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.

robertmoggach
źródło
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.

JA Faucett
źródło
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.

mihai.ciorobea
źródło
3

Możesz użyć operatora &&:

cd myDirectory && ls

Jack Bauer
źródło
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.

Thomasd
źródło
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

Krish
źródło
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)

jithu83
źródło
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.

Chris
źródło
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 .

Gauthier
źródło
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 ).

Lane Roathe
źródło
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

godżilla
źródło
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.

Jurij Nudelman
źródło
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
}
intika
źródło
-2

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

cd somedir; \
pwd
Max
źródło