【问题标题】:Why can't I change directories using "cd" in a script?为什么我不能在脚本中使用“cd”更改目录?
【发布时间】:2010-09-20 07:16:59
【问题描述】:

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

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

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

【问题讨论】:

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

标签: bash


【解决方案1】:

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

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

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

【讨论】:

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

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

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

. proj

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

【讨论】:

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

您脚本中的 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; }
  • 这应该是正确的方式,这样可以使用特殊字符,例如“-”。
【解决方案4】:

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

./myscript.sh

. ./myscript.sh

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

【讨论】:

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

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

创建脚本文件

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

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

#!/bin/sh # 文件 /scripts/mastercode.sh # 别名 cdjava='。 /脚本/cdjava'
  • 我创建了一个启动文件,我在其中转储了所有别名和自定义函数。
  • 然后我将此文件导入到我的 .bashrc 中,以便在每次启动时对其进行设置。

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

然后在 .bashrc 文件的末尾:

源 /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 (如果它不存在的话)。然后只需在此处添加您的别名,重新启动终端即可完成。
【解决方案6】:

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

. script_name

或者,它更具可读性但特定于 shell 的别名 source:

source script_name

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

【讨论】:

    【解决方案7】:

    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
    

    【讨论】:

      【解决方案8】:

      最后使用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
      

      【讨论】:

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

      我使用. <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?
      【解决方案10】:

      只需运行:

      cd /home/xxx/yyy && command_you_want
      

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

          您可以执行以下操作:

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

          编辑:这也可以被“点缀”,以防止创建后续的 shell。

          例子:

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

          【讨论】:

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

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

          1 -

          $ nano ~./bashrc

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

          2-

          $ source ~/.bashrc

          3 -

          $ switchp java

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

          希望有帮助!

          【讨论】:

          • @W.M.你最终可以不带任何参数做“$ switchp”直接进入项目树
          • @workdreamer 您上面描述的函数方法在这里解决了在我的系统中进入具有相同父文件夹的子文件夹的一些麻烦。谢谢。
          【解决方案14】:

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

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

          【讨论】:

          • 在每个目录中都有该符号链接会很麻烦。可以将符号链接放在 $HOME 中,然后执行 'cd ~/my-project'。不过,坦率地说,使用 CDPATH 更简单。
          【解决方案15】:

          您可以结合 Adam & Greg 的别名和点方法来制作更具动态性的东西——

          alias project=". project"
          

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

          【讨论】:

          • 这正是我维护所有脚本并从 bash 调用它并维护当前会话所需要的——这是完美的答案。
          【解决方案16】:

          你可以结合别名和脚本,

          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 => 做一个@ 987654328@转/home/user/projects/java/a/b/c
          【解决方案17】:
          【解决方案18】:

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

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

          重启终端即可输入

          move_me 
          

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

          【讨论】:

            【解决方案19】:

            你可以使用运算符 && :

            cd myDirectory && ls

            【讨论】:

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

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

            【讨论】:

              【解决方案21】:

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

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

              cdproj
              

              cdproj project_name
              

              这是函数定义。

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

              不要忘记来源您的.bash_profile

              【讨论】:

                【解决方案22】:

                这应该做你想做的事。切换到感兴趣的目录(从脚本中),然后生成一个新的 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 意味着你会失去你的历史,它会重新开始。
                【解决方案23】:

                我做了以下事情:

                创建一个名为 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 是保留关键字,因此是一个相当糟糕的名称选择。
                【解决方案24】:

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

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

                Bash 函数:

                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 :) 这是一篇热门帖子的迟到答案,我猜读者没有看到它,感谢您的支持
                【解决方案25】:

                如果您使用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 文件)。

                【讨论】:

                  【解决方案26】:

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

                  alias p='. p'
                  

                  在你的 .bashrc 中

                  【讨论】:

                  • 这是做什么的?看起来它会自行递归运行,尽管我确定你是否在它没有运行它之前运行过它;)
                  【解决方案27】:

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

                  shopt -s cdable_vars
                  

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

                  创建这样一个环境变量:

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

                  你可以使用:

                  cd myjava
                  

                  Other alternatives.

                  【讨论】:

                    【解决方案28】:

                    注意讨论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 的一部分)

                    【讨论】:

                      【解决方案29】:

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

                      你可以使用 cd 而不是使用

                      export PWD=the/path/you/want
                      

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

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

                      【讨论】:

                        【解决方案30】:

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

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

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

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

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

                        【讨论】:

                          猜你喜欢
                          • 2018-01-19
                          • 2017-09-20
                          • 1970-01-01
                          • 1970-01-01
                          • 2019-09-21
                          • 2018-12-26
                          • 2020-01-10
                          相关资源
                          最近更新 更多