不使用source命令运行bash脚本

25
有没有办法标记脚本为“作为源代码运行”,这样就不必每次都添加source或"."命令了?例如,如果我编写了一个名为“sup”的脚本,我希望以以下方式调用它:
sup Argument

与其

source sup Argument
或者
. sup Argument

我想在脚本中使用 cd 命令。


1
请澄清。将脚本设置为可执行文件和在脚本中使用“cd”命令的问题并不相同。 - simon
1
有没有办法标记一个脚本为“作为源代码运行”,这样每次就不必添加源或“.”命令了? - typeoneerror
1
你有两个不同的问题,这就是为什么我要求澄清。你可以使脚本可执行,然后你就不需要"."或"source",但它们将无法修改调用者环境(你从中执行的shell)。所以cd对调用者没有任何改变。这是两个不同的问题。 - simon
4个回答

35

Bash在它或你的内核甚至考虑在其中执行的操作之前就派生并启动了一个子shell。 这不是您可以“撤消”的事情。 因此,不,这是不可能的。

谢天谢地。

相反,请查看bash函数:

sup() {
    ...
}

将它放在你的~/.bashrc中。


25
当您运行shell时,有两种调用shell脚本的方法:
  • Executing a script spawns a new process inside which the script is running. This is done by typing the script name, if it is made executable and starts with a

    #!/bin/bash
    line, or directly invoking
    /bin/bash mycmd.sh

  • Sourcing a script runs it inside its parent shell (i.e. the one you are typing commands into). This is done by typing

    source mycmd.sh
    or
    . mycmd.sh

如果一个没有被引用的脚本中的 cd 操作,是永远不会传递到其父 Shell 的,因为这将违反进程隔离。

如果你只关心 cd 操作,可以使用 cd "快捷方式" 来摆脱脚本... 查看 bash 文档,在 CDPATH 环境变量中了解更多。

否则,您可以使用别名来输入单个命令,而无需使用 source 或 . 。

alias mycmd="source mycmd.sh"

我已经修改了我的CDPATH,但当脚本在它自己的shell中执行时,它似乎仍然无法工作。 - typeoneerror
当然可以,因为更改不会传播到父shell,也就是您正在使用的那个。我的评论更多地是关于一种摆脱脚本的方法。 - Varkhan
太好了!我甚至没有想到可以使用别名。 - Sophie

10
为其创建一个别名:
alias sup=". ~/bin/sup"

或者类似这样。

另请参阅:为什么在bash shell脚本中cd不起作用?


回答counter-example的评论:在Solaris 10上使用Korn Shell进行实验表明,我可以执行:

$ pwd
/work1/jleffler
$ echo "cd /work5/atria" > $HOME/bin/yyy
$ alias yyy=". ~/bin/yyy"
$ yyy
$ pwd
/work5/atria
$

在Solaris 10上使用Bash(3.00.16)的实验结果也显示出相同的行为。



嗯,别名起作用了,但仍在单独的 shell 中执行,因此脚本中的 'cd' 没有任何作用。不过还是谢谢! - typeoneerror
在这种情况下,可能要采用参考问题中的CDPATH解决方案?这是我主要使用的方法。脚本实际上包含什么?只有一个“cd”还是更多的内容? - Jonathan Leffler

8

如果您在调用子shell时,想从当前环境中获取脚本是不可能的。

但是,您可以检查脚本是否被获取,并强制脚本在未被获取时终止:

if [ -z "$PS1" ] ; then
    echo "This script must be sourced. Use \"source <script>\" instead."
    exit
fi

同样地,您可以强制脚本不被作为资源引用,而是作为子shell来执行(保留当前shell环境):
if [ "$PS1" ] ; then
    echo "This script cannot be sourced. Use \"./<script>\" instead."
    return
fi

这两个版本均可作为代码片段(gists)使用:请查看请源码不要源码


1
顺便说一下,echo -e 不是理想的选择 -- 即使 shell 总是 bash,有时它会根据 xpg_echoposix 标志的值而在输出中打印 -e(由于 POSIX 的 echo 规范仅允许对 -n 进行特殊处理,因此严格遵守 POSIX 的 echo 不允许对 -e 执行任何操作,除了在输出中打印它)。请参阅 https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html,尤其是 APPLICATION USAGE 和 RATIONALE 部分。 - Charles Duffy
谢谢,那是一个打字错误,这里没有用。已删除。 - Tim
我会将内容打印到stderr(即>&2 echo 'This script must be sourced.')。否则,这是非常好的建议! - Max Truxa

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