Mengapa "cd" tidak berfungsi di skrip shell?


767

Saya mencoba menulis skrip kecil untuk mengubah direktori saat ini ke direktori proyek saya:

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

Saya menyimpan file ini sebagai proj, menambahkan izin eksekusi chmod, dan menyalinnya ke /usr/bin. Ketika saya menyebutnya dengan:, projia tidak melakukan apa-apa. Apa yang saya lakukan salah?


10
duplikat lintas situs: superuser.com/questions/176783/…
lesmana

Di masa depan Anda selalu dapat mencoba mengujinya pwddi baris terakhir. Jadi sebelum skrip selesai maka Anda dapat memeriksa apakah berfungsi atau tidak ..
sobi3ch

5
@lesmana bagaimana itu duplikat?
aland

@aland Karena OP sebenarnya tidak menjalankan skrip, itu sebabnya direktori kerja tidak berubah untuknya. cdperintah berfungsi dengan baik di dalam skrip, coba sendiri.
Alexander Gonchiy

Jawaban:


635

Script Shell dijalankan di dalam subkulit, dan setiap subkulit memiliki konsep sendiri tentang apa direktori saat ini. The cdberhasil, tetapi segera setelah keluar subkulit, Anda kembali berada di shell interaktif dan tidak pernah berubah di sana.

Salah satu cara untuk mengatasi ini adalah dengan menggunakan alias sebagai gantinya:

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

7
Alias ​​tidak begitu fleksibel untuk dikelola atau diubah. Dalam hal banyak cd, skrip bisa lebih baik.
Thevs

16
Fungsinya lebih fleksibel daripada alias, jadi di situlah Anda akan melihat selanjutnya ketika alias tidak cukup.
ephemient

4
Apakah perlu dicatat bahwa pada MS-DOS, perilaku skrip adalah bahwa skrip yang dipanggil dapat mengubah direktori (dan bahkan drive) dari shell perintah pemanggilan? Dan bahwa Unix tidak memiliki cacat ini?
Jonathan Leffler

23
Jonathan: sementara itu benar, itu tidak benar-benar terkait dengan pertanyaan itu. Jawaban pada SO akan mendapatkan dua kali lebih lama jika mereka masing-masing harus daftar kekurangan yang sesuai di MS-DOS!
Greg Hewgill

17
Cara lain untuk menyiasatinya adalah dengan sumber file skrip: . my-scriptatau source my-script.
HelloGoodbye

504

Anda tidak melakukan kesalahan! Anda telah mengubah direktori, tetapi hanya di dalam subkulit yang menjalankan skrip.

Anda dapat menjalankan skrip dalam proses Anda saat ini dengan perintah "dot":

. proj

Tapi saya lebih suka saran Greg untuk menggunakan alias dalam kasus sederhana ini.


95
.juga dieja source, pilih mana yang Anda anggap lebih berkesan.
ephemient

47
@Efemient: Good pointsource. Itu menjelaskan mengapa ia bekerjasource. Juga membuktikan bahwa kemalasan-bukan keharusan-sering menjadi ibu dari inventionsource
Adam Liss

8
@ephemient: perhatikan bahwa sumber digunakan dalam C shell dan Bash; itu tidak didukung di POSIX atau shell Korn, atau di shell Bourne klasik.
Jonathan Leffler

2
"source" digunakan dalam Z-shell
Nathan Moos

1
@AdamLiss Ini berfungsi dengan baik, tetapi ada satu kelemahan - entah bagaimana perintah source / dot menonaktifkan kemungkinan untuk secara otomatis melengkapi nama skrip / perintah dengan kunci TAB. Masih dimungkinkan untuk menambahkan argumen / input dengan kunci TAB, tetapi sayangnya nama perintah harus dimasukkan secara langsung. Apakah Anda tahu cara membuat kunci TAB berfungsi dalam kasus ini?
Rafal

215

Secara otomatis cdskrip Anda berfungsi saat ia mengubah direktori shell yang menjalankan skrip, tetapi itu adalah proses terpisah yang bercabang dari shell interaktif Anda.

Cara yang kompatibel dengan Posix untuk menyelesaikan masalah ini adalah dengan mendefinisikan prosedur shell daripada skrip perintah yang dipanggil oleh shell .

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

Anda bisa mengetik ini atau memasukkannya ke dalam salah satu dari berbagai file startup shell.


6
Saya setuju, ini jawaban terbaik, terbentur. Selain itu, alias mungkin sesuai dalam beberapa situasi tetapi jika ia mencoba menggunakan cd dalam skrip dan ingin menambahkan hal lain ke dalamnya, alias tidak akan berguna.
Justin Buser

4
Ini terlihat seperti solusi! Dan saya mencoba untuk mengimplementasikannya, tetapi tidak berkinerja :( inilah skrip saya: #! / Bin / bash jhome () {echo "please" cd / home echo "work"} jhome
Gant Laborde

1
@ ManMan, Anda harus menambahkan ini dalam "file startup shell", seperti ~ / .bashrc
Jackie Yeh

3
Anda juga dapat menggunakan argumen (atau dua ...), yaitu:jhome(){ cd /home/tree/projects/$1; }
irbanana

1
Ini harus dengan cara yang benar, ini memungkinkan untuk menggunakan karakter khusus, seperti "-" misalnya.
bangkokguy

159

Ini cddilakukan di dalam shell skrip. Ketika skrip berakhir, shell itu keluar, dan kemudian Anda ditinggalkan di direktori Anda. "Sumber" skrip, jangan jalankan. Dari pada:

./myscript.sh

melakukan

. ./myscript.sh

(Perhatikan titik dan spasi sebelum nama skrip.)


2
Ini keren, dan mungkin baik untuk diketahui. Apa yang dilakukan yang terakhir persis (bagaimana cara kerjanya)? Ini mungkin solusi terbaik di utas ini.
Jonah

2
fwiw, di bagian komentar dari jawaban Adam Liss, ephemient menjawab pertanyaan tentang apa '.' adalah. Itu adalah hal yang sama dengansource
g19fanatic

1
Hati-hati, "sumber" adalah bashisme. yaitu dasbor (default Debian untuk / bin / sh) tidak mendukungnya, sementara "." bekerja seperti yang diharapkan.
allo

Jawaban bagus! juga jika myscript.shada dalam direktori yang termasuk dalam $ PATH, Anda dapat sumbernya dari mana saja tanpa menentukan path lengkap.
CodeBrew

Ini bekerja untuk saya yang terbaik. Solusi ini adalah yang paling sederhana (+1).
HuserB1989

96

Untuk membuat skrip bash yang akan di-cd ke direktori tertentu:

Buat file skrip

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

Kemudian buat alias di file startup Anda.

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

  • Saya membuat file startup tempat saya membuang semua alias dan fungsi khusus saya.
  • Lalu saya sumber file ini ke .bashrc saya untuk mengaturnya pada setiap boot.

Sebagai contoh, buat file aliases / functions master: /scripts/mastercode.sh
(Masukkan alias dalam file ini.)

Kemudian di akhir file .bashrc Anda :

source /scripts/mastercode.sh



Sekarang mudah untuk cd ke direktori java Anda, cukup ketik cdjava dan Anda berada di sana.


2
file mastercode.shtidak memerlukan shabang ( #!/bin/sh), karena tidak (dan tidak bisa) dieksekusi dalam sebuah subkulit. Tetapi pada saat yang sama, Anda perlu mendokumentasikan "rasa" dari file ini; misalnya, ksh atau bash (atau (t) csh / zsh, dll), dan hampir pasti tidak benar-benar sh. Saya biasanya menambahkan komentar (tetapi bukan shebang) untuk mengomunikasikan ini; mis., "file ini dimaksudkan bersumber (dari bash), tidak dijalankan sebagai skrip shell."
michael

Kiat lain (untuk bash): jika Anda menggunakan variabel dalam skrip Anda, maka saat skrip sedang sourced (melalui alias), variabel-variabel tersebut akan bocor ke lingkungan shell Anda. Untuk menghindarinya, lakukan semua pekerjaan skrip dalam suatu fungsi, dan panggil saja di akhir skrip. Dalam fungsi tersebut, deklarasikan variabel apa pun local.
Rhubbarb

di ubuntu cukup buat ~ / .bash_aliases (jika belum ada). Kemudian tambahkan saja alias Anda di sana, restart terminal dan Anda selesai.
Vlad

51

Anda dapat menggunakan .untuk menjalankan skrip di lingkungan shell saat ini:

. script_name

atau sebagai alternatif, alias lebih mudah dibaca tetapi spesifik shell source:

source script_name

Ini menghindari subkulit, dan memungkinkan variabel atau bawaan apa pun (termasuk cd) untuk memengaruhi shell saat ini.


38

Gagasan Jeremy Ruten untuk menggunakan symlink memicu pemikiran yang belum melewati jawaban lain. Menggunakan:

CDPATH=:$HOME/projects

Usus besar penting; itu berarti bahwa jika ada direktori 'dir' di direktori saat ini, maka ' cd dir' akan berubah menjadi itu, daripada melompat ke tempat lain. Dengan nilai yang ditetapkan seperti yang ditunjukkan, Anda dapat melakukan:

cd java

dan, jika tidak ada sub-direktori yang disebut java di direktori saat ini, maka ia akan membawa Anda langsung ke $ HOME / proyek / java - tidak ada alias, tidak ada skrip, tidak ada administrator yang meragukan atau perintah dot.

$ HOME saya adalah / Pengguna / jleffler; $ CDPATH saya adalah:

:/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

31

Gunakan exec bashdi akhir

Skrip bash beroperasi pada lingkungannya saat ini atau di lingkungan anak-anaknya, tetapi tidak pernah di lingkungan orang tuanya.

Namun, pertanyaan ini sering ditanyakan karena seseorang ingin dibiarkan pada bash prompt (baru) di direktori tertentu setelah eksekusi skrip bash dari direktori lain.

Jika ini masalahnya, jalankan saja instance bash anak di akhir skrip:

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

16
Ini menciptakan subkulit baru. Saat Anda mengetik, exitAnda akan kembali ke shell tempat Anda menjalankan skrip ini.
tripleee

1
Ketika skrip dipanggil beberapa kali tampaknya itu membuat shell bersarang dan saya perlu mengetik 'keluar' berkali-kali. Mungkinkah ini diselesaikan?
VladSavitsky

Lihat jawaban lain: gunakan alias, gunakan "pushd / popd / dirs", atau gunakan "source"
qneill

25

Saya mendapatkan kode saya untuk bekerja dengan menggunakan. <your file name>

./<your file name> Dosis tidak berfungsi karena tidak mengubah direktori Anda di terminal, itu hanya mengubah direktori khusus untuk skrip itu.

Ini program saya

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

Ini terminal saya

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

2
Apa perbedaan antara . somethingdan ./something?? Jawaban ini berhasil untuk saya dan saya tidak mengerti mengapa.
Dracorat

. somethingmemungkinkan Anda menjalankan skrip dari lokasi mana pun, ./somethingmengharuskan Anda berada di direktori tempat file disimpan.
Projjol


Bisakah Anda meletakkan titik di garis shebang? #!. /bin/bash?
Sigfried


12

Saat Anda menjalankan skrip shell, ia menjalankan instance baru dari shell itu ( /bin/bash). Dengan demikian, skrip Anda hanya menjalankan shell, mengubah direktori dan keluar. Dengan kata lain, cd(dan perintah lain semacam itu) dalam skrip shell tidak mempengaruhi atau tidak memiliki akses ke shell dari mana mereka diluncurkan.


11

Pada kasus khusus saya, saya perlu terlalu banyak kali untuk mengubah untuk direktori yang sama. Jadi di .bashrc saya (saya menggunakan ubuntu) saya telah menambahkan

1 -

$ nano ~. / bashrc

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

2-

$ source ~ / .bashrc

3 -

$ switchp java

Secara langsung itu akan dilakukan: cd / home / tree / proyek / java

Semoga itu bisa membantu!


1
@ WM Anda akhirnya dapat melakukan "$ switchp" tanpa parameter apa pun untuk langsung menuju pohon proyek
workdreamer

2
@ workdreamer Pendekatan fungsi yang Anda jelaskan di atas menyelesaikan sedikit masalah di sini untuk masuk ke sub-folder yang memiliki folder induk yang sama di sistem saya. Terima kasih.
WM

10

Anda dapat melakukan hal berikut:

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

EDIT: Ini bisa 'bertitik' juga, untuk mencegah pembuatan kerang berikutnya.

Contoh:

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

1
Itu menjadi agak berantakan setelah beberapa kali berjalan .. Anda harus exit(atau ctrl + d) beberapa kali untuk keluar dari shell, misalnya .. Sebuah alias jauh lebih bersih (bahkan jika perintah shell menghasilkan direktori, dan itu cd ke output - alias something = "cd getnewdirectory.sh")
dbr

Perhatikan 'exec'. Itu membuat mengganti shell lama.
Thevs

2
Eksekutif hanya mengganti sub-shell yang menjalankan perintah cd, bukan shell yang menjalankan skrip. Seandainya Anda membuat skrip, maka Anda akan benar.
Jonathan Leffler

Jadikan 'titik-titik' - tidak ada masalah. Saya baru saja mengusulkan solusi. Itu tidak tergantung pada bagaimana Anda meluncurkan ini.
Thevs

2
Hehe, saya pikir lebih baik hanya dengan 'dot' satu-baris script dengan hanya perintah cd :) Saya akan menjaga jawaban saya ... Itu akan menjadi jawaban bodoh yang benar untuk pertanyaan bodoh yang salah :)
Thevs

7

Itu hanya mengubah direktori untuk skrip itu sendiri, sementara direktori Anda saat ini tetap sama.

Anda mungkin ingin menggunakan tautan simbolik sebagai gantinya. Ini memungkinkan Anda untuk membuat "jalan pintas" ke file atau direktori, jadi Anda hanya perlu mengetikkan sesuatu seperti cd my-project.


Memiliki symlink di setiap direktori akan menjadi gangguan. Dimungkinkan untuk meletakkan symlink di $ HOME dan kemudian melakukan 'cd ~ / my-project'. Terus terang, lebih mudah menggunakan CDPATH.
Jonathan Leffler

7

Anda dapat menggabungkan pendekatan alias dan titik Adam & Greg untuk membuat sesuatu yang bisa lebih dinamis—

alias project=". project"

Sekarang menjalankan alias proyek akan menjalankan skrip proyek di shell saat ini sebagai lawan dari subkulit.


Ini adalah persis apa yang saya butuhkan untuk mempertahankan semua skrip saya dan hanya memanggilnya dari bash dan mempertahankan sesi saat ini - ini adalah jawaban yang SEMPURNA.
Matt The Ninja

7

Anda dapat menggabungkan alias dan skrip,

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

dengan ketentuan bahwa skrip menggemakan jalur tujuan. Perhatikan bahwa itu adalah backticks yang mengelilingi nama skrip. 

Misalnya, skrip Anda bisa jadi

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

Keuntungan dengan teknik ini adalah bahwa skrip dapat mengambil sejumlah parameter baris perintah dan memancarkan tujuan yang berbeda yang dihitung oleh logika yang mungkin kompleks.


3
Mengapa? Anda bisa menggunakan: proj() { cd "/home/user/projects/java/$1"; }=> proj "foo"(atau, proj "foo bar"<= jika Anda memiliki spasi) ... atau bahkan (misalnya): proj() { cd "/home/user/projects/java/$1"; shift; for d; do cd "$d"; done; }=> proj a b c=> melakukan suatu cdke/home/user/projects/java/a/b/c
michael


5

Dalam file ~ / .bash_profile Anda. tambahkan fungsi selanjutnya

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

Mulai ulang terminal dan Anda dapat mengetik

move_me 

dan Anda akan dipindahkan ke folder tujuan.


3

Anda dapat menggunakan operator &&:

cd myDirectory && ls


Downvote: Ini tidak berusaha menjawab pertanyaan aktual di sini. Anda dapat menjalankan dua perintah saat diminta (dengan &&jika Anda ingin yang kedua bersyarat pada yang pertama, atau hanya dengan ;atau baris baru di antara mereka) tetapi menempatkan ini dalam skrip akan membawa Anda kembali ke "mengapa induk shell tidak menggunakan direktori baru ketika cdsebenarnya berhasil? "
tripleee

3

Meskipun sumber skrip yang ingin Anda jalankan adalah salah satu solusi, Anda harus menyadari bahwa skrip ini kemudian dapat secara langsung mengubah lingkungan shell Anda saat ini. Juga tidak mungkin untuk mengajukan argumen lagi.

Cara lain untuk dilakukan, adalah mengimplementasikan skrip Anda sebagai fungsi dalam bash.

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

Teknik ini digunakan oleh autojump: http://github.com/joelthelion/autojump/wiki untuk memberi Anda belajar bookmark direktori shell.


3

Anda dapat membuat fungsi seperti di bawah ini di Anda .bash_profiledan itu akan berfungsi dengan lancar.

Fungsi berikut mengambil parameter opsional yang merupakan proyek. Misalnya, Anda bisa menjalankannya

cdproj

atau

cdproj project_name

Berikut ini definisi fungsinya.

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

Jangan lupa untuk sumber .bash_profile


1
jawaban yang jauh lebih baik daripada alias
Pax0r

Solusi ini brilian dalam kesederhanaan dan fleksibilitasnya.
Kjartan

3

Ini harus melakukan apa yang Anda inginkan. Ubah ke direktori yang menarik (dari dalam skrip), dan kemudian menelurkan bash shell baru.

#!/bin/bash

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

Jika Anda menjalankan ini, itu akan membawa Anda ke direktori yang menarik dan ketika Anda keluar itu akan membawa Anda kembali ke tempat semula.

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

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

Ini bahkan akan membawa Anda untuk kembali ke direktori asli Anda ketika Anda keluar ( CTRL+ d)


3
Memunculkan bash baru akan berarti Anda kehilangan riwayat dan itu akan mulai baru.
Tisch

2

LOOOOOng kali setelahnya, tetapi saya melakukan yang berikut:

buat file bernama case

rekatkan yang berikut dalam file:

#!/bin/sh

cd /home/"$1"

simpan lalu:

chmod +x case

Saya juga membuat alias di .bashrc:

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

sekarang ketika saya mengetik:

case 12345

intinya saya mengetik:

cd /home/12345

Anda dapat mengetik folder apa pun setelah 'huruf':

case 12

case 15

case 17

yang seperti mengetik:

cd /home/12

cd /home/15

cd /home/17

masing-masing

Dalam kasus saya jalannya jauh lebih lama - orang-orang ini menyimpulkannya dengan ~ info sebelumnya.


1
The cdalias adalah berlebihan dan janggal; alias seharusnya '. ~ / case` sebagai gantinya. Juga casemerupakan kata kunci yang dipesan, jadi pilihan yang agak buruk untuk sebuah nama.
tripleee

1

Anda tidak memerlukan skrip, hanya mengatur opsi yang benar dan membuat variabel lingkungan.

shopt -s cdable_vars

di Anda ~/.bashrcmemungkinkan untuk cdkonten variabel lingkungan.

Buat variabel lingkungan seperti itu:

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

dan Anda dapat menggunakan:

cd myjava

Alternatif lain .


0

Jika Anda menggunakan ikan sebagai cangkang Anda, solusi terbaik adalah membuat fungsi. Sebagai contoh, diberikan pertanyaan asli, Anda dapat menyalin 4 baris di bawah ini dan menempelkannya ke baris perintah ikan Anda:

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

Ini akan membuat fungsi dan menyimpannya untuk digunakan nanti. Jika proyek Anda berubah, ulangi saja proses menggunakan jalur baru.

Jika suka, Anda dapat menambahkan file fungsi secara manual dengan melakukan hal berikut:

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

dan masukkan teks:

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

dan akhirnya tekan ctrl + x untuk keluar dan y diikuti dengan kembali untuk menyimpan perubahan Anda.

( CATATAN: metode pertama menggunakan funcsave membuat file proj.fish untuk Anda ).


0

Saya punya skrip bash sederhana bernama p untuk mengelola perubahan direktori pada
github.com/godzilla/bash-stuff
cukup letakkan skrip di direktori bin lokal Anda (/ usr / local / bin)
dan masukkan

alias p='. p'

di .bashrc Anda


apa fungsinya? sepertinya itu akan berjalan secara rekursif, meskipun saya yakin jika Anda sudah menjalankannya sebelum itu tidak;)
Ryan Taylor


0

Ini adalah pertanyaan lama, tapi saya sangat terkejut saya tidak melihat trik ini di sini

Alih-alih menggunakan cd Anda bisa menggunakan

export PWD=the/path/you/want

Tidak perlu membuat subshell atau menggunakan alias.

Perhatikan bahwa Anda bertanggung jawab untuk memastikan / path / Anda / inginkan ada.


Sayangnya, tidak berhasil.
ttfreeman

0

Seperti yang dijelaskan pada jawaban lain, Anda telah mengubah direktori, tetapi hanya di dalam sub-shell yang menjalankan skrip . ini tidak memengaruhi shell induk.

Salah satu solusinya adalah menggunakan fungsi bash alih-alih skrip bash ( sh); dengan menempatkan kode skrip bash Anda ke suatu fungsi. Itu membuat fungsi tersedia sebagai perintah dan kemudian, ini akan dieksekusi tanpa proses anak dan dengan demikian cdperintah apa pun akan berdampak pada shell penelepon.

Fungsi bash:

Salah satu fitur dari profil bash adalah untuk menyimpan fungsi kustom yang dapat dijalankan di terminal atau dalam skrip bash dengan cara yang sama Anda menjalankan aplikasi / perintah ini juga dapat digunakan sebagai jalan pintas untuk perintah yang panjang.

Untuk membuat sistem Anda berfungsi efisien secara luas, Anda perlu menyalin fungsi Anda di akhir beberapa file

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

Anda dapat sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profilemengedit / membuat file-file itu dengan cepat

Howto:

Salin kode skrip bash Anda di dalam fungsi baru di akhir file profil bash Anda dan mulai ulang terminal Anda, Anda kemudian dapat menjalankan cddatau apa pun fungsi yang Anda tulis.

Contoh Skrip

Membuat pintasan cd ..dengancdd

cdd() {
  cd ..
}

ls jalan pintas

ll() {
  ls -l -h
}

ls jalan pintas

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

-2

Anda dapat menjalankan beberapa baris dalam subkulit yang sama jika Anda mengakhiri garis dengan garis miring terbalik.

cd somedir; \
pwd
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.