シェルスクリプトで「cd」が機能しないのはなぜですか?


766

現在のディレクトリをプロジェクトディレクトリに変更する小さなスクリプトを書こうとしています。

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

このファイルをprojとして保存しchmod、で実行権限を追加して、にコピーしました/usr/bin。私がそれを次のように呼ぶと、 proj何もしません。何が悪いのですか?


10
クロスサイト重複:superuser.com/questions/176783/...
lesmana

将来はいつでもpwd最後の行でテストしてみることができます。だから、スクリプト仕上げの前に、あなたはそれが動作するかしない確認することができます。..
sobi3ch

5
@lesmana複製はどうですか?
2016年

@aland OPは実際にはスクリプトを実行しないため、作業ディレクトリが変更されません。cdコマンドはスクリプト内でうまく機能します。自分で試してください。
Alexander Gonchiy 2016年

回答:


634

シェルスクリプトはサブシェル内で実行され、各サブシェルには、現在のディレクトリが何かという独自の概念があります。はcd成功しますが、サブシェルが終了するとすぐにインタラクティブシェルに戻り、何も変更されません。

これを回避する1つの方法は、代わりにエイリアスを使用することです。

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

7
エイリアスは、管理や変更がそれほど柔軟ではありません。多くの「CD」の場合、スクリプトの方が優れている可能性があります。
Thevs

16
関数はエイリアスよりも柔軟性が高いため、エイリアスでは不十分な場合に次に検討する必要があります。
ephemient 2008年

4
MS-DOSでは、呼び出されたスクリプトが呼び出し元のコマンドシェルのディレクトリ(さらにはドライブ)を変更できるというのが、MS-DOSではスクリプトの動作でした。そして、そのUnixにはこの欠陥はありませんか?
ジョナサンレフラー

23
ジョナサン:それは本当ですが、それは質問とは実際には関係ありません。SOの回答は、MS-DOSの対応する欠陥をそれぞれリストする必要がある場合、2倍長くなります。
グレッグ・ヒューギル

17
それを回避する別の方法は、スクリプトファイルをソースにすることです:. my-scriptまたはsource my-script
HelloGoodbye 2015年

503

あなたは何も悪いことをしていません!ディレクトリを変更しましたが、スクリプトを実行するサブシェル内のみです。

「ドット」コマンドを使用して、現在のプロセスでスクリプトを実行できます。

. proj

ただし、この単純なケースでは、エイリアスを使用することをGregの提案でお勧めします。


95
.も綴られていsourceます。覚えやすい方を選択してください。
ephemient 2008年

47
@エフェミエント:それが機能する理由を説明する優れたポイントソースソースもまた、怠惰(必要ではない)がしばしば発明ソースの母であることを証明します
Adam Liss

8
@ephemient:ソースはCシェルとBashで使用されることに注意してください。POSIXシェルやKornシェル、従来のBourneシェルではサポートされていません。
Jonathan Leffler、

2
「ソース」はZシェルで使用されています
Nathan Moos 2013年

1
@AdamLissうまく機能しますが、欠点が1つあります。ソース/ドットコマンドは、TABキーを使用してスクリプト/コマンドの名前を自動補完する可能性を無効にします。TABキーで引数や入力を追加することも可能ですが、残念ながらコマンド名を直接入力する必要があります。この場合、TABキーを機能させる方法を知っていますか?
Rafal 2014年

215

cd技術的にはあなたのスクリプトで働いていました、それはスクリプトを実行したシェルのディレクトリを変更すると、それはあなたの対話型シェルからフォーク別のプロセスでした。

この問題を解決するPosix互換の方法は、シェルが呼び出すコマンドスクリプトではなく、シェルプロシージャを定義することです。

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

これを入力するか、さまざまなシェルスタートアップファイルの1つに置くことができます。


6
私は同意します、これは最高の答えです、ぶつけられました。また、エイリアスは状況によっては適切な場合がありますが、スクリプトでcdを使用しようとしていて、他に何かを追加したい場合、エイリアスは役に立ちません。
Justin Buser 2013年

4
これは解決策のようです!そして、私はそれを実装しようとしていますが、実行されていません:(これが私のスクリプトです:#!/ bin / bash jhome(){echo "please" cd / home echo "work"} jhome
Gant Laborde

1
@GantMan、これを〜/ .bashrcのような「シェルスタートアップファイル」に追加する必要があります
Jackie Yeh

3
引数(または2つ)を使用することもできます。例:jhome(){ cd /home/tree/projects/$1; }
irbanana '12

1
これは正しい方法である必要があります。これにより、たとえば「-」などの特殊文字を使用できるようになります。
bangkokguy

159

cdスクリプトのシェル内で行われます。スクリプトが終了すると、そのシェルは終了し、元のディレクトリに残ります。スクリプトを「ソース」し、実行しないでください。の代わりに:

./myscript.sh

行う

. ./myscript.sh

(スクリプト名の前のドットとスペースに注意してください。)


2
これはクールで、おそらく知っておくと良いでしょう。後者は正確には何をしますか(それはどのように機能しますか)?これはおそらく、このスレッドでの最良の解決策です。
ジョナ

2
fwiw、Adam Lissの回答のコメントセクションで、ephemientは「。」です。それは同じですsource
g19fanatic

1
注意してください、「ソース」はバシズムです。つまり、ダッシュ(/ bin / shのDebianデフォルト)はサポートしませんが、「。」期待どおりに動作します。
allo 10/05/17

正解です。またmyscript.sh、$ PATHに含まれるディレクトリにある場合は、フルパスを指定せずにどこからでもソースを入手できます。
CodeBrew、

これは私にとって最も効果的でした。このソリューションが最も簡単です(+1)。
HuserB1989

96

選択ディレクトリに移動するbashスクリプトを作成するには:

スクリプトファイルを作成する

#!/ bin / sh
#ファイル:/ scripts / cdjava
#
cd / home / askgelal / projects / java

次に、起動ファイルにエイリアスを作成します。

#!/ bin / sh
#ファイル/scripts/mastercode.sh
#
エイリアスcdjava = '。/ scripts / cdjava '

  • すべてのエイリアスとカスタム関数をダンプするスタートアップファイルを作成しました。
  • 次に、このファイルを.bashrcに読み込んで、起動ごとに設定します。

たとえば、マスターのエイリアス/関数ファイルを作成します:/scripts/mastercode.sh
(このファイルにエイリアスを入力します)。

次に、.bashrcファイルの最後に次のように入力します。

ソース/scripts/mastercode.sh



javaディレクトリにcdするのは簡単です。cdjavaと入力するだけです。


2
サブシェルでは実行されない(そして実行できない)mastercode.shため、ファイルにはシャバン(#!/bin/sh)は必要ありません。ただし、同時に、このファイルのシェル「フレーバー」を文書化する必要があります。たとえば、kshまたはbash(または(t)csh / zshなど)であり、実際にはそうではありませんsh。私は通常、これを伝えるためにコメントを追加します(シバンではありません)。たとえば、「このファイルは(bashから)提供されることを意図しており、シェルスクリプトとしては実行されません。」
マイケル2014年

別のヒント(bashの場合):スクリプトで変数を使用すると、スクリプトがsource(を介してalias)dになるため、それらの変数がシェル環境にリークします。これを回避するには、関数内でスクリプトのすべての作業を行い、スクリプトの最後で呼び出すだけです。関数内で、変数を宣言しますlocal
Rhubbarb 2015

ubuntuでは〜/ .bash_aliasesを作成するだけです(まだ存在しない場合)。次に、エイリアスをそこに追加し、ターミナルを再起動して完了です。
Vlad

51

を使用.して、現在のシェル環境でスクリプトを実行できます。

. script_name

あるいは、より読みやすいがシェル固有のエイリアスsource

source script_name

これにより、サブシェルが回避され、変数または組み込み(を含むcd)が現在のシェルに影響を与えることができます。


38

Jeremy Rutenのシンボリックリンクの使用のアイデアは、他のどの回答とも一致しない考えを引き起こしました。使用する:

CDPATH=:$HOME/projects

先頭のコロンは重要です。これは、現在のディレクトリに「dir」というディレクトリがある場合、「cd dir」が別の場所に移動するのではなく、「」に変更されることを意味します。図のように値を設定すると、次のことができます。

cd java

また、現在のディレクトリにjavaというサブディレクトリがない場合は、$ HOME / projects / javaに直接移動します-エイリアス、スクリプト、疑わしいexecやドットコマンドはありません。

私の$ HOMEは/ Users / jlefflerです。私の$ CDPATHは:

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

exec bash最後に使う

bashスクリプトは、現在の環境またはその子の環境で動作しますが、親環境では動作しません。

ただし、別のディレクトリからbashスクリプトを実行した後、特定のディレクトリで(新しい)bashプロンプトを表示したままにしたいため、この質問がよく寄せられます

この場合は、スクリプトの最後で子bashインスタンス実行するだけです。

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

16
これにより、新しいサブシェルが作成されます。入力exitすると、このスクリプトを実行したシェルに戻ります。
tripleee

1
スクリプトが複数回呼び出されたとき、ネストされたシェルが作成されるようで、何度も「exit」と入力する必要があります。これはどういうわけか解決できますか?
VladSavitsky

他の回答を参照してください:エイリアスを使用する、「pushd / popd / dirs」を使用する、または「ソース」を使用する
qneill

25

私は私のコードを使用して動作させました. <your file name>

./<your file name> 端末のディレクトリは変更されないため、機能しません。そのスクリプトに固有のディレクトリが変更されるだけです。

これが私のプログラムです

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

これが私の端末です

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

2
違いは何だ. somethingとは./something?? この答えは私にとってうまくいきました、そして私はその理由を理解していません。
Dracorat

. something、あなたが任意の場所からスクリプトを実行することを可能にする./somethingファイルが保存されているディレクトリにある必要があります。
Projjol


シバン行にドットを入れることができますか?#!. /bin/bash
シグフライド


12

シェルスクリプトを起動すると、そのシェルの新しいインスタンスが実行されます(/bin/bash)。したがって、スクリプトはシェルを起動し、ディレクトリを変更して終了します。別の言い方をするcdと、シェルスクリプト内の(および他のそのようなコマンド)は、それらが起動されたシェルに影響を与えたり、アクセスしたりすることはありません。


11

私の特定のケースでは、同じディレクトリに変更するのに何度も必要でした。だから私の.bashrc(私はubuntuを使用しています)に私は追加しました

1-

$ nano〜。/ bashrc

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

2-

$ソース〜/ .bashrc

3-

$ switchp java

直接行います:cd / home / tree / projects / java

お役に立てば幸いです。


1
@WM最終的には、パラメーターなしで "$ switchp"を実行して、プロジェクトツリーに直接移動できます
workdreamer

2
@workdreamer上記で説明した関数アプローチにより、システムで同じ親フォルダーを持つサブフォルダーに移動する際の小さな問題が解決しました。ありがとうございました。
WM 2016

10

以下を実行できます。

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

編集:後続のシェルの作成を防ぐために、これを「ドット」にすることもできます。

例:

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

1
数回実行すると、かなり面倒になります。exitたとえば、シェルを終了するには、数回(またはctrl + d)クリックする必要があります。たとえば、エイリアスは非常にきれいです(シェルコマンドがディレクトリを出力し、それがcd出力へgetnewdirectory.sh
-alias

「exec」に注意してください。古いシェルを置き換えます。
Thevs、2008年

2
execは、cdコマンドを実行していたサブシェルのみを置き換え、スクリプトを実行したシェルは置き換えません。スクリプトを点在させたなら、あなたは正しいでしょう。
ジョナサンレフラー、

「ドット」にします-問題ありません。私は解決策を提案しました。起動方法には依存しません。
Thevs、2008年

2
Hehe、私はcdコマンドだけで1行のスクリプトを 'ドット'する方がいいと思います:)私はとにかく私の答えを保持します...それは間違った愚かな質問に対する正しい愚かな答えになります:)
Thevs

7

現在のディレクトリは同じままですが、スクリプト自体のディレクトリのみが変更されます。

代わりにシンボリックリンクを使用することもできます。これにより、ファイルまたはディレクトリへの「ショートカット」を作成できるため、のように入力するだけで済みますcd my-project


そのシンボリックリンクをすべてのディレクトリに置くのは厄介です。シンボリックリンクを$ HOMEに配置して、「cd〜/ my-project」を実行することもできます。率直に言って、CDPATHを使用する方が簡単です。
ジョナサンレフラー、

7

Adam&Gregのエイリアスとドットアプローチを組み合わせて、よりダイナミックなものを作ることができます—

alias project=". project"

プロジェクトエイリアスを実行すると、サブシェルではなく現在のシェルでプロジェクトスクリプトが実行されます。


これは、すべてのスクリプトを維持し、bashから呼び出して現在のセッションを維持するために必要なものです。これが完全な答えです。
Matt The Ninja

7

エイリアスとスクリプトを組み合わせることができます。

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

スクリプトが宛先パスをエコーする場合。これらはスクリプト名を囲むバッククォートであることに注意してください。 

たとえば、スクリプトは次のようになります。

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

この手法の利点は、スクリプトが任意の数のコマンドラインパラメーターを受け取り、複雑なロジックで計算された異なる宛先を出力できることです。


3
どうして?次のように使用できます:proj() { cd "/home/user/projects/java/$1"; }=> proj "foo"(または、proj "foo bar"スペースがある場合は<=)...または(たとえば):proj() { cd "/home/user/projects/java/$1"; shift; for d; do cd "$d"; done; }=> proj a b c=>はaを実行cdします/home/user/projects/java/a/b/c
michael


5

〜/ .bash_profileファイル内。次の機能を追加

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

端末を再起動すると、次のように入力できます

move_me 

移動先のフォルダに移動します。


3

演算子&&を使用できます。

cd myDirectory && ls


反対票:これは、ここでの実際の質問への回答を試みるものではありません。プロンプトで2つのコマンドを実行できます(&&2番目のコマンドを最初の条件付きにする;か、それらの間にまたは改行のみを付ける場合)。これをスクリプトに入れると、「親シェルが使用しない理由」に戻ります。cd実際に成功したときの新しいディレクトリ?」
tripleee

3

実行するスクリプトを入手することは1つの解決策ですが、このスクリプトは現在のシェルの環境を直接変更できることに注意してください。また、引数を渡すこともできなくなりました。

もう1つの方法は、スクリプトをbashの関数として実装することです。

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

この手法はautojump:http ://github.com/joelthelion/autojump/wikiで使用され、学習シェルディレクトリブックマークを提供します。


3

以下のような関数を自分で作成でき、.bash_profileスムーズに動作します。

次の関数は、プロジェクトであるオプションのパラメーターを取ります。たとえば、あなたはただ走ることができます

cdproj

または

cdproj project_name

これが関数の定義です。

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

を調達することを忘れないでください .bash_profile


1
エイリアスよりもはるかに良い回答
Pax0r

このソリューションは、そのシンプルさと柔軟性に優れています。
Kjartan

3

これはあなたが望むことをするはずです。(スクリプト内から)目的のディレクトリに移動し、新しいbashシェルを起動します。

#!/bin/bash

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

これを実行すると、目的のディレクトリに移動し、終了すると元の場所に戻ります。

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

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

これにより、終了時に元のディレクトリに戻ることもできます(CTRL+ d


3
新しいbashを生成すると、履歴が失われ、新しく開始されます。
ティッシュ2018年

2

しばらくしてから、次のようにしました。

ケースと呼ばれるファイルを作成する

以下をファイルに貼り付けます。

#!/bin/sh

cd /home/"$1"

それを保存してから:

chmod +x case

私もエイリアスを作成しました.bashrc

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

今私が入力すると:

case 12345

基本的に私は入力しています:

cd /home/12345

'case'の後に任意のフォルダーを入力できます。

case 12

case 15

case 17

これはタイピングのようなものです:

cd /home/12

cd /home/15

cd /home/17

それぞれ

私の場合、パスははるかに長くなります-これらの人はそれを以前の〜情報でまとめました。


1
cd別名で余分と無粋です。エイリアスは単に 'でなければなりません。代わりに〜/ case`。またcase、予約済みのキーワードであるため、名前の選択はやや不十分です。
Tripleee、2015

1

スクリプトは必要ありません。正しいオプションを設定し、環境変数を作成するだけです。

shopt -s cdable_vars

あなたの環境変数の内容に~/.bashrc許可しcdます。

そのような環境変数を作成します。

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

そしてあなたは使うことができます:

cd myjava

他の選択肢


0

をシェルとして使用している場合、最良の解決策は関数を作成することです。例として、元の質問が与えられた場合、以下の4行をコピーして、fishコマンドラインに貼り付けることができます。

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

これにより関数が作成され、後で使用できるように保存されます。プロジェクトが変更された場合は、新しいパスを使用してプロセスを繰り返します。

必要に応じて、次の操作を行って関数ファイルを手動で追加できます。

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

テキストを入力してください:

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

最後にctrl + xを押して終了し、yに戻って変更を保存します。

注:funcsaveを使用する最初の方法では、proj.fishファイルが作成されます)。


0


github.com/godzilla/bash-stuffでディレクトリの変更を管理するためのpという単純なbashスクリプト
があります。スクリプトをローカルのbinディレクトリ(/ usr / local / bin)
に配置して、

alias p='. p'

あなたの.bashrc


これは何をしますか?再帰的に実行されるように見えますが、実行前に実行したかどうかは確かです;)
Ryan Taylor

0

議論に注意してください、私は親プロセスの作業ディレクトリを設定するにはどうすればよいですか?

これは、いくつかのハック答え、例えば含ま https://stackoverflow.com/a/2375174/755804(GDBを経由して親プロセスのディレクトリを変更し、これを行わない)とhttps://stackoverflow.com/a/51985735/755804を(親プロセスの入力ストリームにcd dirnametailcd挿入するコマンド。まあ、理想的にはハックではなくbashの一部である必要があります)


0

古い質問ですが、このトリックがここにないのは本当に驚きです

cdを使用する代わりに、

export PWD=the/path/you/want

サブシェルを作成したり、エイリアスを使用したりする必要はありません。

/ path / you / wantが存在することを確認するのはあなたの責任であることに注意してください。


残念ながら機能しませんでした。
ttfreeman

0

他の回答で説明したように、ディレクトリを変更しましたが、スクリプトを実行するサブシェル内のみですです。これは親シェルには影響しません。

1つの解決策は、bashスクリプト()の代わりにbash関数を使用することshです。bashスクリプトコードを関数に配置する。これにより、関数がコマンドとして使用できるようになり、子プロセスなしで実行されます。cdコマンドは呼び出し元のシェルに影響を与えます。

バッシュ機能:

bashプロファイルの1つの機能は、アプリケーションまたはコマンドを実行するのと同じ方法で、ターミナルまたはbashスクリプトで実行できるカスタム関数を格納することです。これは、長いコマンドのショートカットとしても使用できます。

関数の効率的なシステムを広くするには、いくつかのファイルの最後に関数をコピーする必要があります

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

あなたはできるsudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile編集に/すぐにそれらのファイルを作成します

方法 :

bashのプロファイルファイルの最後にある新しい関数内にbashスクリプトコードをコピーしてターミナルを再起動すると、実行しcddた関数または記述した関数を実行できます。

スクリプトの例

ショートカットを作成cd ..してcdd

cdd() {
  cd ..
}

lsショートカット

ll() {
  ls -l -h
}

lsショートカット

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

-2

行をバックスラッシュで終了すると、同じサブシェルでいくつかの行を実行できます。

cd somedir; \
pwd
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.