为什么“ cd”在shell脚本中不起作用?


766

我正在尝试编写一个小脚本以将当前目录更改为我的项目目录:

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

我将此文件另存为proj,使用添加了执行权限chmod,然后将其复制到/usr/bin。当我通过以下方式调用它时 proj,它什么也没做。我究竟做错了什么?



将来,您总是可以尝试pwd在最后一行进行测试。所以脚本之前完成,那么你可以检查它正在正常工作或不..
sobi3ch

5
@lesmana怎么重复?
2013年

@aland因为OP实际上并不运行脚本,所以这就是工作目录不会更改的原因。cd命令在脚本内部运行良好,请自己尝试。
亚历山大·贡奇

Answers:


634

Shell脚本在一个子Shell中运行,每个子Shell对当前目录都有自己的概念。的cd成功,但只要子shell退出,你是在交互式背壳和从来都没有改变,因此。

解决此问题的一种方法是改用别名:

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

7
别名在管理或更改方面不太灵活。如果有很多“ cd”,脚本可能会更好。
Thevs

16
函数比别名更灵活,因此别名不足时,您将在这里查找。
迅速

4
值得注意的是,在MS-DOS上,脚本的行为是被调用的脚本可以更改调用命令外壳的目录(甚至驱动器)?而且那个Unix没有这个缺陷吗?
乔纳森·勒夫勒

23
乔纳森:的确如此,但这与问题无关。如果他们必须列出MS-DOS中的相应缺陷,那么对SO的答案将获得两倍的时间!
Greg Hewgill

17
解决该问题的另一种方法是获取脚本文件:. my-scriptsource my-script
HelloGoodbye 2015年

503

你没做错什么!您已经更改了目录,但仅在运行脚本的子外壳中进行了更改。

您可以使用“点”命令在当前进程中运行脚本:

. proj

但是我更希望格雷格的建议在这种简单情况下使用别名。


95
.也是拼写的source,请选择您觉得更难忘的一个。
短暂的

47
@Ephemient:很好的pointsource解释了它为什么起作用的sourcesource还证明了懒惰(不是必需)经常是发明源的母亲
Adam Liss

8
@ephemient:请注意,源代码用于C shell和Bash;POSIX或Korn外壳程序或经典Bourne外壳程序均不支持该功能。
乔纳森·勒夫勒

2
Z壳中使用“来源”
Nathan Moos,

1
@AdamLiss效果很好,但是有一个缺点-source / dot命令以某种方式禁止使用TAB键自动完成脚本/命令名称的可能性。仍然可以使用TAB键添加参数/输入,但是不幸的是,该命令的名称需要直接输入。您知道在这种情况下如何使TAB键起作用吗?
拉法尔

215

cd脚本中的技术工作,因为它改变了运行脚本的外壳的目录,但是这是从你的交互shell分叉一个单独的进程。

解决此问题的Posix兼容方法是定义Shell过程而不是Shell调用命令脚本

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

您可以只输入它或将其放在各种Shell启动文件之一中。


6
我同意,这是最好的答案。同样,在某些情况下,别名可能是合适的,但是如果他试图在脚本中使用cd并想向其中添加其他任何内容,则别名是没有用的。
贾斯汀·布斯克2013年

4
这看起来像一个解决方案!而且我正在尝试实现它,但是没有执行:(这是我的脚本:#!/ bin / bash jhome(){echo“ please” cd / home echo“ work”}} jhome
甘特·

1
@GantMan,您应该将其添加到“ shell启动文件”中,例如〜/ .bashrc
Jackie Yeh

3
您还可以使用一个(或两个)参数,即:jhome(){ cd /home/tree/projects/$1; }
irbanana

1
这应该是正确的方法,这样可以使用特殊字符,例如“-”。
bangkokguy

159

cd是在脚本的外壳中完成的。脚本结束时,该外壳程序退出,然后将您留在原来的目录中。“来源”脚本,不要运行它。代替:

./myscript.sh

. ./myscript.sh

(请注意脚本名称前的点和空格。)


2
这很酷,可能很高兴知道。后者到底是做什么的(它是如何工作的)?这可能是此线程上的最佳解决方案。
约拿(Jonah)2012年

2
接下来,在亚当·利斯(Adam Liss)的回答的评论部分中,短暂的回答了“什么”的问题。是。它与source
g19fanatic

1
注意,“来源”是一种bashism。即破折号(/ bin / sh的Debian默认设置)不支持,而“。”。可以正常工作。
allo

好答案!如果myscript.sh位于$ PATH中包含的目录中,您也可以从任何地方获取它,而无需指定完整路径。
CodeBrew

这对我来说是最好的。该解决方案是最简单的(+1)。
HuserB1989

96

制作一个bash脚本,将其cd到选择目录:

创建脚本文件

#!/ 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



现在可以轻松地将cd插入您的java目录,只需键入cdjava即可。


2
该文件mastercode.sh不需要shabang(#!/bin/sh),因为它不是(也不能)在子shell中执行。但是同时,您确实需要记录该文件的外壳“风味”;例如ksh或bash(或(t)csh / zsh等),几乎可以肯定不是sh。我通常会添加评论(但不要添加shebang)来传达此信息;例如,“该文件旨在(从bash中获取)而不是作为shell脚本运行。”
迈克尔(Michael)2014年

另一个技巧(对于bash):如果在脚本中使用变量,那么在脚本sourced中(通过alias),这些变量将泄漏到您的shell环境中。为了避免这种情况,请在函数中完成脚本的所有工作,然后在脚本末尾调用它。在函数内,声明任何变量local
Rhubbarb 2015年

在ubuntu中,只需创建〜/ .bash_aliases(如果尚不存在)。然后只需在此处添加别名,重新启动终端即可。
弗拉德

51

您可以用来.在当前Shell环境中执行脚本:

. script_name

或者,其更具可读性但特定于Shell的别名source

source script_name

这样可以避免使用subshel​​l,cd而是允许任何变量或内建函数(包括)影响当前shell。


38

杰里米·鲁滕(Jeremy Ruten)使用符号链接的想法引发了一个没有其他答案的想法。采用:

CDPATH=:$HOME/projects

冒号很重要。这意味着如果当前目录中存在目录“ dir”,则“ cd dir”将更改为该目录,而不是跳到其他位置。按照所示设置值,您可以执行以下操作:

cd java

并且,如果当前目录中没有名为java的子目录,那么它将直接带您到$ HOME / projects / java-没有别名,没有脚本,没有可疑的exec或dot命令。

我的$ 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

30

使用exec bash

bash脚本在其当前环境或其子级环境上运行,但绝不在其父级环境上运行。

但是,经常会问这个问题,因为一个人想要在执行后留在某个目录中的(新)bash提示符下另一个目录中 bash脚本,。

如果是这种情况,只需在脚本末尾执行一个子bash实例

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

16
这将创建一个新的子外壳。键入时,exit您将返回运行该脚本的外壳程序。
tripleee

1
当脚本被多次调用时,它似乎会创建嵌套的shell,我需要多次键入“ exit”。可以通过某种方式解决吗?
VladSavitsky

查看其他答案:使用别名,使用“ pushd / popd / dirs”或使用“ source”
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


你能把点放在shebang行吗?#!. /bin/bash
Sigfried


12

当您启动Shell脚本时,它将运行该Shell()的实例/bin/bash。因此,您的脚本仅启动外壳程序,更改目录并退出。换句话说,cdshell脚本中的(以及其他此类命令)既不影响也不能访问从其启动的shell。


11

在我的特殊情况下,我需要太多次才能更改同一目录。因此,在我的.bashrc(我使用ubuntu)上,我添加了

1-

$ nano〜。/ bashrc

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

2

$ source〜/ .bashrc

3-

$ switchp java

直接可以执行:cd / home / tree / projects / java

希望有帮助!


1
@WM您最终可以在没有任何参数的情况下直接执行“ $ switchp”,直接进入项目树
workdreamer '16

2
@workdreamer上面介绍的函数方法在进入我的系统中具有相同父文件夹的子文件夹时,解决了一些麻烦。谢谢。
WM

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)多次退出外壳。到输出-别名something =“ cd getnewdirectory.sh”)
dbr

注意“ exec”。它可以代替旧壳。
Thevs

2
exec仅替换运行cd命令的子外壳,而不替换运行脚本的外壳。如果您点缀了脚本,那么您是正确的。
乔纳森·莱夫勒

使其“加点”-没问题。我只是提出了解决方案。它不取决于您如何启动它。
Thevs

2
呵呵,我认为最好只用cd命令对单行脚本加点:)无论如何我都会保留答案...对于不正确的愚蠢问题,这将是正确的愚蠢答案:)
Thevs

7

它仅更改脚本本身的目录,而当前目录保持不变。

您可能要改用符号链接。它允许您为文件或目录创建“快捷方式”,因此只需键入即可cd my-project


在每个目录中都有该符号链接将是一件麻烦事。可以将符号链接放在$ HOME中,然后执行“ cd〜/ my-project”。坦白说,使用CDPATH更简单。
乔纳森·勒夫勒

7

您可以将Adam&Greg的别名和点方法结合起来,以制作出更具动态感的内容-

alias project=". project"

现在运行项目别名将在当前外壳程序(而不是子外壳程序)中执行项目脚本。


这正是我需要维护所有脚本并仅从bash调用它并维护当前会话所需的内容-这是完美的答案。
马特·忍者

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=>没有一个cd/home/user/projects/java/a/b/c
迈克尔


5

在您的〜/ .bash_profile文件中。添加下一个功能

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

重新启动终端,您可以键入

move_me 

然后您将被移至目标文件夹。


3

您可以使用运算符&&:

cd myDirectory && ls


Downvote:这并不试图在此处回答实际问题。您可以在提示符处运行两个命令(&&如果希望第二个命令以第一个为条件,或者仅;在它们之间使用或换行符),但是将其放在脚本中将使您回到“为什么父shell不使用cd实际成功时的新目录?”
2013年

3

采购要运行的脚本是一种解决方案,但应注意,此脚本可以直接修改当前Shell的环境。同样,不可能再传递参数了。

另一种方法是在bash中将脚本实现为函数。

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

autojump使用此技术:http ://github.com/joelthelion/autojump/wiki ,为您提供学习Shell目录书签。


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

该解决方案的简单性和灵活性非常出色。
贾尔坦

3

这应该做您想要的。转到感兴趣的目录(从脚本内),然后生成一个新的bash shell。

#!/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
产生新的冲击将意味着您失去历史记录,并且会重新开始。
蒂施

2

LOOOOOng时间过后,但我做了以下事情:

创建一个名为case的文件

将以下内容粘贴到文件中:

#!/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也是一个保留关键字,因此名称的选择相当糟糕。
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

我有一个名为p的简单bash脚本,用于管理
github.com/godzilla/bash-stuff上的目录更改,
只需将脚本放在您的本地bin目录(/ usr / local / bin)中,
然后将

alias p='. p'

在您的.bashrc中


这是做什么的?看起来它会递归运行,尽管我确定您是否已经运行过它;)
Ryan Taylor


0

这是一个老问题,但我真的很惊讶,我在这里看不到这个窍门

除了使用cd外,您还可以使用

export PWD=the/path/you/want

无需创建子外壳或使用别名。

请注意,您有责任确保/ path / you / want存在。


不幸的是,它没有工作。
ttfreeman

0

如其他答案所述,您已经更改了目录,但仅在运行脚本子外壳中进行了更改。这不会影响父外壳。

一种解决方案是使用bash函数代替bash脚本(sh);通过将bash脚本代码放入函数中。这使得该功能可以作为命令使用,然后将在没有子进程的情况下执行该函数,因此任何cd命令都将影响调用程序外壳程序。

重击功能:

bash配置文件的一个功能是存储自定义功能,这些功能可以在终端或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

如果以反斜杠结束行,则可以在同一子Shell中执行一些行。

cd somedir; \
pwd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.