从Bash脚本中更改当前目录

246

如何从脚本中更改当前目录?

我想在Bash中创建一个目录导航工具。我已经创建了一个测试脚本,代码如下:

#!/bin/bash
cd /home/artemb

当我从Bash shell执行脚本时,当前目录不会改变。是否可能从脚本中更改当前shell目录?


2
只是一个增强建议:如果您使用 pushd(可能重定向到 >/dev/null 以抑制其输出),而不是 cd,则可以稍后使用 popd 返回到先前的目录。 - mklement0
可能是为什么在Bash shell脚本中“cd”不起作用?的重复问题。 - Roland
17个回答

274
当您启动脚本时,将创建一个仅继承您环境的新进程。当它结束时,它就会结束。您当前的环境保持不变。
相反,您可以像这样启动脚本:
. myscript.sh

.会在当前环境中计算脚本,因此它可能会被改变。


7
因为你是正确的所以点赞。但我真的怀疑他每次需要更改目录时是否愿意寻找一个目录更改脚本。此外,“.sh”扩展名真的很丑,不要使用它们。 - lhunath
4
通常最好不要那样使用它。大多数情况下,你会很高兴你的当前环境没有受到影响。但是我有一些脚本来为我完成一些设置任务,包括切换到正确的位置,然后我也会运行它们。顺便说一句,.sh当然是个人风格的问题。在系统范围安装脚本时,我可能不会使用它们。但在我的~/bin目录中,我使用它们来知道每个脚本的用途 :) - Norbert Hartl
14
为什么以.sh为扩展名的文件看起来很丑陋? - Carl Pritchett
1
@CarlPritchett 命令名称扩展被视为有害 - gniourf_gniourf
4
对我而言,那篇文章的推理是无效的。似乎结果事先已知,但找到理由并不容易。因此,我坚持认为这是个人风格问题,没有其他原因。 - Norbert Hartl
显示剩余3条评论

208

您需要将脚本转换为shell函数:

#!/bin/bash
#
# this script should not be run directly,
# instead you need to source it from your .bashrc,
# by adding this line:
#   . ~/bin/myprog.sh
#

function myprog() {
  A=$1
  B=$2
  echo "aaa ${A} bbb ${B} ccc"
  cd /proc
}

原因是每个进程都有自己的当前目录,当你从shell执行程序时,它会在一个新进程中运行。标准的"cd"、"pushd"和"popd"是内置于shell解释器中的,因此它们会影响shell进程。

通过将你的程序作为一个shell函数来创建,你添加了自己的进程内命令,然后任何目录更改都会反映在shell进程中。


2
你会如何在tcsh中实现这个? - joedborg
4
我注意到一个问题,就是在文件中我们必须在调用函数之前先定义函数。这可能会对像我这样缺乏经验的人有所帮助。 - Bhushan
2
一旦我创建了myprog.sh文件并将其添加到.bashrc中,我该如何运行脚本?我应该从另一个shell文件中调用我的函数还是直接从shell中调用?似乎两种方式都不起作用... - Andrea Silvestri
1
@AndreaSilvestri 要么注销并重新登录,要么执行source .bashrc命令。将其添加到.bashrc文件中,在运行.bashrc之前不会产生任何效果。 - Jon Strayer
2
@winden,请避免教授使用function关键字,尤其是在名称后面结合()``。function myfunc{是旧版ksh语法,bash为向后兼容而实现的;myfunc() {是现代POSIX语法,所有符合标准的shell都必须支持;function myfunc() {`将两者合并在一起,与旧版ksh和现代POSIX sh均不兼容。请参见https://wiki.bash-hackers.org/scripting/obsolete。 - Charles Duffy
显示剩余3条评论

62
鉴于答案的难以阅读和过度复杂化,我认为请求者应该这样做:
  1. 将该脚本添加到 PATH
  2. . 脚本名称 的形式运行该脚本

.(点号)将确保脚本不在子 shell 中运行。


51

综上所述,您可以创建一个别名

alias your_cmd=". your_cmd"

如果你不想每次在脚本中引用shell环境时都要写上前导的 ".",或者你只是不想记住这必须要做,才能使脚本正确工作。


这个会在重启后保持不变吗? - SDsolar
1
@SDsolar 你需要将以下内容添加到你的~/.bashrc文件中以使其持久化。对我来说运行得很好。 - Rafał G.
可以放置在 ~/.bash_aliases 中 - Champ
当Bash启动时,@Champ~/.bash_aliases并不总是被调用。 - Mateusz Piotrowski

35

如果您正在使用bash,可以尝试使用别名:

将以下行添加到.bashrc文件中:

alias p='cd /home/serdar/my_new_folder/path/'
当你在命令行中输入"p"时,它将会改变当前目录。

2
+1 代表别名。Shell 函数很有趣,但 OP 要求一个简单的导航器来使用 cd 命令。我猜大多数人需要像这样的脚本来浏览源代码分支,而别名已经足够了。 - Brad Dre

23
如果你运行一个bash脚本,它将在当前环境或其子进程中运行,永远不会影响父进程。
如果你的目标是运行命令:goto.sh /home/test 那么在/home/test中交互的一种方法是在你的脚本中运行一个bash交互子shell:
#!/bin/bash
cd $1
exec bash

这样你就会在/home/test目录下,直到你退出(使用exit或Ctrl+C)这个shell。


我以为这会解决我正在运行的shell脚本的问题,但实际上它启动了shell并忘记了我想要的东西。 - vwvan
1
这个在我Mac上(zsh shell)使用 exec zsh 而不是 exec bash 对我很有用。谢谢。 - Amit Yadav

20

使用 pushd 命令会将当前目录压入目录堆栈,并将其更改为给定的目录。使用 popd 命令会获取堆栈顶部的目录并切换到该目录。

pushd ../new/dir > /dev/null
# do something in ../new/dir
popd > /dev/null

6

只需前往

yourusername/.bashrc (or yourusername/.bash_profile on MAC) by an editor

并将此代码添加到最后一行旁边:

alias yourcommand="cd /the_path_you_wish"

然后退出编辑器。

然后输入:

source ~/.bashrc or source ~/.bash_profile on MAC.

现在你可以在终端中使用yourcommand

哦,是的,我忘记了,别名已经是我多年来解决这个问题的方法。 - Sridhar Sarnobat

6
在你的shell脚本中添加以下cd命令行:
exec $SHELL

3

基本上我们使用cd..回到每个目录。我想通过给出需要一次性返回的目录数来使它更加容易。您可以使用别名命令使用单独的脚本文件来实现这一点。例如:

code.sh

#!/bin/sh
 _backfunc(){
 if [ "$1" -eq 1 ]; then
  cd ..
 elif [ "$1" -eq 2 ]; then
  cd ../..
 elif [ "$1" -eq 3 ]; then
  cd ../../..
 elif [ "$1" -eq 4 ]; then
  cd ../../../..
 elif ["$1" -eq 10]; then
  cd /home/arun/Documents/work
 fi
 }
alias back='_backfunc'   

在当前 shell 使用 source code.sh 后,您可以使用:

$back 2 

要回到当前目录的上两级,请参考此处详细说明。该网页还介绍了如何将代码放入~/.bashrc中,以便每次打开新shell时都自动具备此新别名命令。您可以通过添加更多if条件和不同的参数来修改代码,以添加新的命令以进入特定目录。您也可以从此处获取git代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接