ChatGPT解决这个技术问题 Extra ChatGPT

为什么我不能在脚本中使用“cd”更改目录?

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

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

我将此文件保存为 proj,使用 chmod 添加执行权限,并将其复制到 /usr/bin。当我通过 proj 调用它时,它什么也不做。我究竟做错了什么?

以后您可以随时尝试在最后一行使用 pwd 对其进行测试。因此,在脚本完成之前,您可以检查它是否有效..
@lesmana 那是怎么重复的?
@aland 因为 OP 实际上 not 确实运行脚本,这就是为什么工作目录不会为他改变的原因。 cd 命令在脚本中运行良好,请自行尝试。

c
compie

Shell 脚本在子shell 中运行,每个子shell 都有自己的当前目录概念。 cd 成功,但一旦子 shell 退出,您就会回到交互式 shell,那里没有任何变化。

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

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

别名不太灵活,难以管理或更改。如果有许多'cd',脚本可能会更好。
函数比别名更灵活,所以当别名不够用时,你接下来要看的地方。
值得注意的是,在 MS-DOS 上,脚本的行为是被调用的脚本可以更改调用命令 shell 的目录(甚至驱动器)?那Unix就没有这个缺陷吗?
乔纳森:虽然这是真的,但它与问题并没有真正的关系。如果每个人都必须列出 MS-DOS 中的相应缺陷,那么关于 SO 的答案将获得两倍的时间!
绕过它的另一种方法是获取脚本文件:. my-scriptsource my-script
A
Adam Liss

你没有做错任何事!您已经更改了目录,但仅在运行脚本的子shell 中。

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

. proj

但我更喜欢 Greg 的建议,在这种简单的情况下使用别名。


. 也拼写为 source,选择您认为更难忘的那个。
@Ephemient:很好的点源这解释了它为什么起作用源也证明了懒惰——而不是必要——往往是发明之母
@ephemient:注意 source 在 C shell 和 Bash 中使用; POSIX 或 Korn shell 不支持它,经典的 Bourne shell 也不支持。
"source" 用于 Z-shell
@AdamLiss 它工作得很好,但有一个缺点 - 不知何故,源/点命令禁用了使用 TAB 键自动完成脚本/命令名称的可能性。仍然可以使用 TAB 键添加参数/输入,但不幸的是需要直接输入命令的名称。您知道在这种情况下如何使 TAB 键起作用吗?
D
DigitalRoss

您的脚本中的 cd 在技术上工作,因为它更改了运行脚本的 shell 的目录,但这是一个从交互式 shell 派生的单独进程。

解决这个问题的一种与 Posix 兼容的方法是定义一个 shell 过程,而不是一个 shell 调用的命令脚本。

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

您可以输入或将其放入各种 shell 启动文件之一。


我同意,这是最好的答案,碰到了。此外,在某些情况下,别名可能是合适的,但如果他试图在脚本中使用 cd 并想向其中添加任何其他内容,则别名将毫无用处。
这看起来像一个解决方案!我正在尝试实现它,但它没有执行:(这是我的脚本:#!/bin/bash jhome() { echo "please" cd /home echo "work" } jhome
@GantMan,您应该将其添加到“shell 启动文件”中,例如 ~/.bashrc
您还可以使用一个参数(或两个...),即:jhome(){ cd /home/tree/projects/$1; }
这应该是正确的方法,这使得使用特殊字符成为可能,例如“-”。
B
Boann

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

./myscript.sh

. ./myscript.sh

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


这很酷,可能很高兴知道。后者到底做了什么(它是如何工作的)?这可能是这个线程上最好的解决方案。
fwiw,在 Adam Liss 回答的评论部分,ephemient 回答了“。”是什么的问题。是。它与 source 相同
请注意,“来源”是一种bashism。即破折号(Debian 默认为 /bin/sh)不支持它,而 "."按预期工作。
很好的答案!此外,如果 myscript.sh 位于 $PATH 中包含的目录中,您可以从任何地方获取它,而无需指定完整路径。
这对我来说是最好的。这个解决方案是最简单的(+1)。
M
Matt Thomas

要制作一个将 cd 到一个选择目录的 bash 脚本:

创建脚本文件

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

然后在您的启动文件中创建一个别名。

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

我创建了一个启动文件,我在其中转储了所有别名和自定义函数。

然后我将此文件源到我的 .bashrc 中,以便在每次启动时设置它。

例如,创建一个主别名/函数文件:/scripts/mastercode.sh(将别名放在此文件中。)

然后在 .bashrc 文件的末尾:

source /scripts/mastercode.sh

现在很容易 cd 到您的 java 目录,只需键入 cdjava 即可。


文件 mastercode.sh 不需要 shabang (#!/bin/sh),因为它不是(也不能)在子 shell 中执行。但同时,您确实需要记录此文件的外壳“风味”;例如,ksh 或 bash(或 (t)csh/zsh 等),几乎可以肯定它实际上不是 sh。我通常会添加评论(但不是 shebang)来传达这一点;例如,“此文件旨在获取(来自 bash),而不是作为 shell 脚本运行。”
另一个提示(针对 bash):如果您在脚本中使用变量,那么当脚本被 sourced(通过 alias)时,这些变量将泄漏到您的 shell 环境中。为避免这种情况,请在函数中完成脚本的所有工作,并在脚本末尾调用它。在函数中,声明任何变量 local
在 ubuntu 中只需创建 ~/.bash_aliases (如果它不存在)。然后只需在那里添加您的别名,重新启动终端并完成。
t
that other guy

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

. script_name

或者,它更易读但特定于 shell 的别名 source

source script_name

这避免了子 shell,并允许任何变量或内置函数(包括 cd)改为影响当前 shell。


J
Jonathan Leffler

Jeremy Ruten 使用符号链接的想法引发了一个尚未跨越任何其他答案的想法。利用:

CDPATH=:$HOME/projects

前导冒号很重要;这意味着如果当前目录中有目录“dir”,那么“cd dir”将更改为该目录,而不是跳到其他地方。使用如图所示设置的值,您可以执行以下操作:

cd java

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

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

S
Serge Stroobandt

最后使用 exec bash

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

但是,这个问题经常被问到,因为在从另一个目录中执行 bash 脚本之后,希望留在某个目录中的(新)bash 提示符处。

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

#!/usr/bin/env bash
cd /home/tree/projects/java
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
exec bash

要返回上一个父 bash 实例,请使用 Ctrl+D

更新

至少对于较新版本的 bash,最后一行的 exec 不再需要。此外,通过使用 $SHELL 环境变量,可以使脚本与任何首选的 shell 一起工作。然后给出:

#!/usr/bin/env bash
cd desired/directory
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
$SHELL

这将创建一个新的子外壳。当您键入 exit 时,您将返回到运行此脚本的 shell。
当脚本被多次调用时,它似乎会创建嵌套的 shell,我需要多次输入“exit”。这能以某种方式解决吗?
查看其他答案:使用别名、使用“pushd/popd/dirs”或使用“source”
@Acumenus你是绝对正确的。旧版本的 bash 和可能的其他 shell 需要 exec。我相应地更新了答案。
运行 exec bash 后,我可以留在脚本中并继续运行其他命令吗?
k
kaelhop

我使用. <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$ 

. something./something 有什么区别??这个答案对我有用,我不明白为什么。
. something 允许您从任何位置运行脚本,./something 要求您位于文件所在的目录中。
你能把点放在shebang线上吗? #!. /bin/bash
w
warhansen

只需运行:

cd /home/xxx/yyy && command_you_want

D
Daniel Spiewak

当您触发 shell 脚本时,它会运行该 shell (/bin/bash) 的 new 实例。因此,您的脚本只是启动一个 shell,更改目录并退出。换句话说,shell 脚本中的 cd(和其他此类命令)不会影响也无法访问启动它们的 shell。


T
Thevs

您可以执行以下操作:

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

编辑:这也可以“点缀”,以防止创建后续外壳。

例子:

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

运行几次后就会变得相当混乱。例如,您将不得不多次 exit(或 ctrl+d)退出 shell。别名非常干净(即使 shell 命令输出一个目录,它的输出是 cd - alias something="cd getnewdirectory.sh")
请注意“执行”。它可以替换旧外壳。
exec 只替换运行 cd 命令的子 shell,而不是运行脚本的 shell。如果你在脚本上加点,那么你是正确的。
呵呵,我认为最好只用 cd 命令“点”一行脚本 :) 无论如何我都会保留我的答案......这将是对不正确愚蠢问题的正确愚蠢答案 :)
exec /bin/bash 做了我需要的。谢谢!
w
workdreamer

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

-

$纳米〜./bashrc

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

2-

$ 源 ~/.bashrc

3 -

$ switchp java

直接它会做: cd /home/tree/projects/java

希望有帮助!


@WM你最终可以不带任何参数的“$ switchp”直接进入项目树
@workdreamer 您上面描述的功能方法在这里解决了在我的系统中进入具有相同父文件夹的子文件夹的一些麻烦。谢谢你。
P
Paige Ruten

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

您可能想改用 symbolic link。它允许您为文件或目录创建“快捷方式”,因此您只需键入 cd my-project 之类的内容。


在每个目录中都有那个符号链接会很麻烦。可以将符号链接放在 $HOME 中,然后执行 'cd ~/my-project'。不过,坦率地说,使用 CDPATH 更简单。
r
rjmoggach

你可以结合 Adam & Greg 的别名和 dot 方法来制作更动态的东西——

alias project=". project"

现在运行项目别名将在当前 shell 中执行项目脚本,而不是子 shell。


这正是我需要维护我的所有脚本并从 bash 调用它并维护当前会话 - 这是完美的答案。
k
knuton

您可以结合别名和脚本,

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

前提是脚本回显目标路径。请注意,这些是围绕脚本名称的反引号。

例如,您的脚本可能是

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

这种技术的优点是脚本可以采用任意数量的命令行参数并发出由可能复杂的逻辑计算的不同目的地。


为什么?你可以使用: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
m
mihai.ciorobea

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

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

重启终端,你可以输入

move_me 

您将被移动到目标文件夹。


J
Jack Bauer

您可以使用运算符 && :

cd myDirectory && ls


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

虽然采购您想要运行的脚本是一种解决方案,但您应该知道该脚本可以直接修改当前 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 目录书签。


t
tripleee

您可以在 .bash_profile 中创建如下所示的函数,它会顺利运行。

以下函数采用一个可选参数,它是一个项目。例如,您可以运行

cdproj

或者

cdproj project_name

这是函数定义。

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

不要忘记来源您的 .bash_profile


D
Developer Guy

这应该做你想要的。切换到感兴趣的目录(从脚本中),然后生成一个新的 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)


产生一个新的 bash 将意味着你失去你的历史,它会重新开始。
A
Asclepius

我做了以下事情:

创建一个名为 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

分别

在我的情况下,这条路要长得多——这些家伙早先用 ~ 信息总结了它。


别名中的 cd 是多余且不雅的;别名应该只是 '. ~/case` 代替。此外,case 是保留关键字,因此是一个相当糟糕的名称选择。
i
intika

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

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

重击功能:

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 或您编写的任何函数。

脚本示例

使用 cdd 创建到 cd .. 的快捷方式

cdd() {
  cd ..
}

ls 快捷方式

ll() {
  ls -l -h
}

ls 快捷方式

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

这真的有效。我很惊讶它没有得到应有的支持。
@EerikSvenPuudist :) 这是一篇热门帖子的迟到答案,我猜读者没有看到它,感谢您的支持
L
Lane Roathe

如果您使用 fish 作为 shell,最好的解决方案是创建一个函数。例如,给定原始问题,您可以复制下面的 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 文件)。


g
godzilla

我有一个名为 p 的简单 bash 脚本来管理目录更改
github.com/godzilla/bash-stuff
只需将脚本放入本地 bin 目录 (/usr/local/bin)
并放入

alias p='. p'

在你的 .bashrc


这是做什么的?看起来它会自行递归运行,尽管我确定你是否在它没有运行它之前运行过它;)
C
Community

您不需要脚本,只需设置正确的选项并创建环境变量。

shopt -s cdable_vars

在您的 ~/.bashrc 中允许 cd 到环境变量的内容。

创建这样一个环境变量:

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

你可以使用:

cd myjava

Other alternatives


1
18446744073709551615

注意讨论How do I set the working directory of the parent process?

它包含一些骇人听闻的答案,例如 https://stackoverflow.com/a/2375174/755804(通过 gdb 更改父进程目录,不要这样做)和 https://stackoverflow.com/a/51985735/755804(将 cd dirname 注入输入流的命令 tailcd父进程的;嗯,理想情况下它应该是 bash 而不是 hack 的一部分)


Y
Yuri Nudelman

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

而不是使用 cd 你可以使用

export PWD=the/path/you/want

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

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


Z
ZakS

我必须在 tcsh 中工作,我知道这不是一个优雅的解决方案,但是例如,如果我必须将文件夹更改为一个单词不同的路径,那么整个事情都可以在别名中完成

a alias_name 'set a = `pwd`; set b = `echo $a | replace "Trees" "Tests"` ; cd $b'

如果路径总是固定的,那么

a alias_name2 'cd path/you/always/need'

应该可以在上面的行中,设置了新的文件夹路径