【问题标题】:File execution with dot space versus dot slash点空间与点斜线的文件执行
【发布时间】:2014-08-09 01:18:30
【问题描述】:

我正在尝试使用现有的代码库,但遇到了问题。简而言之,我执行了一个shell 脚本(我们称之为A),它的第一步 是调用另一个脚本(B)。脚本B 在我的当前目录中(我正在使用的程序的要求)。该软件的手册引用了bash,但是A 中的cmets 建议它是在ksh 中开发的。到目前为止,我一直在bash 工作。

A 内部,执行B 的行很简单:

. B

它使用“点空间”语法来调用程序。它并没有像sudo 那样做任何不寻常的事情。

当我在没有点空间语法的情况下调用A 时,即:

./A

总是出错,提示找不到文件B。我在A 中添加了pwdlswhoamiecho $SHELLecho $PATH 行以进行调试,并确认B 实际上就在那里,脚本以相同的@987654342 运行@ 因为我在命令提示符下,脚本和我是同一个用户,并且脚本和我有相同的搜索路径 $PATH。我还验证了我是否这样做:

. B

在命令行,它工作得很好。但是,如果我将A 中的语法更改为:

./B

相反,A 会成功执行。

同样,如果我使用点空间语法执行A,那么. B./B 都可以工作。

总结
./A 仅适用于 A 包含 ./B 语法。
. A 适用于 A./B. B句法。

我知道使用点空间(即. A)语法在不分叉到子shell的情况下执行,但我不明白这会如何导致我观察到的行为,因为文件显然就在那里。关于语法或父/子进程工作区的细微差别,我是否遗漏了什么?魔法?

UPDATE1:添加了表明该脚本可能是在ksh 中开发的信息,而我正在使用bash
UPDATE2:添加了检查验证$PATH 是否相同。

UPDATE3:脚本说它是为ksh 编写的,但它在bash 中运行。响应 Kenster 的回答,我发现在命令行运行 bash -posix 然后 . B 失败。这表明命令行和脚本之间的环境差异在于后者在符合 POSIX 的模式下运行bash,而命令行不是。再靠近一点,我在bash man 页面看到了这个:

当作为 sh 调用时,bash 在读取启动文件后进入 posix 模式。

Ashebang 确实是 #!/bin/sh

总之,当我在没有点空格语法的情况下运行A 时,它会分叉到它自己的子shell,它处于POSIX 兼容模式,因为shebang#!/bin/sh(而不是例如#!/bin/bash。这是导致A 无法找到B 的命令行和脚本运行环境之间的关键区别。

【问题讨论】:

  • B 是否可执行? . B 在技术上不是 execute B,它是 sources 它。如果其中之一是ksh,请尝试使用ksh yourscript
  • 是的,B 是可执行的。我不清楚两者之间的区别,现在将阅读。
  • 没关系,我看到 source 是在同一个 shell 中运行某些东西的术语(和命令)。

标签: linux bash shell operators ksh


【解决方案1】:

让我们从命令路径的工作原理和使用时间开始。当您运行如下命令时:

ls /tmp

这里的ls 不包含/ 字符,因此shell 在您的命令路径(PATH 环境变量的值)中的目录中搜索名为ls 的文件。如果找到,它会执行该文件。对于ls,它通常位于/bin/usr/bin,并且这两个目录通常都在您的路径中。

当您在命令词中发出带有 / 的命令时:

/bin/ls /tmp

shell 不搜索命令路径。它专门查找文件/bin/ls 并执行该文件。

运行 ./A 是运行名称中带有 / 的命令的示例。 shell 不搜索命令路径;它专门查找名为./A 的文件并执行该文件。 “。”是您当前工作目录的简写,因此./A 指的是应该在您当前工作目录中的文件。如果文件存在,它将像任何其他命令一样运行。例如:

cd /bin
./ls

可以运行/bin/ls

运行. A采购文件的一个示例。获取的文件必须是包含 shell 命令的文本文件。它由当前 shell 执行,无需启动新进程。要获取的文件的查找方式与查找命令的方式相同。如果文件名包含 /,则 shell 会读取您命名的特定文件。如果文件名不包含 /,则 shell 在命令路径中查找它。

. A        # Looks for A using the command path, so might source /bin/A for example
. ./A      # Specifically sources ./A

因此,您的脚本尝试执行 . B 并未能声称 B 不存在,即使您当前目录中有一个名为 B 的文件。如上所述,shell 会在您的命令路径中搜索B,因为B 不包含任何/ 字符。搜索命令时,shell 不会自动搜索当前目录。如果当前目录是命令路径的一部分,它只会搜索当前目录。

简而言之,. B 可能会失败,因为您没有“。” (当前目录)在您的命令路径中,并且尝试获取 B 的脚本假设“。”是你路径的一部分。在我看来,这是脚本中的一个错误。很多人没有“。”。在他们的路径中,脚本不应该依赖于此。

编辑:

您说脚本使用ksh,而您正在使用bash。 Ksh 遵循 POSIX 标准——实际上,KSH 是 POSIX 标准的基础——并且总是按照我的描述搜索命令路径。 Bash 有一个名为“POSIX 模式”的标志,用于控制它遵循 POSIX 标准的严格程度。当不在 POSIX 模式下时——这是人们通常使用的方式——如果在命令路径中找不到文件,bash 将检查当前目录以查找要获取的文件。

如果您要在该 bash 实例中运行 bash -posix 并运行 . B,您应该会发现它不起作用。

【讨论】:

  • 您说得对,当前目录不在$PATH 中。我曾考虑过这一点,但是为什么. B 在命令行中工作?我试过echo $PATH,脚本和我的都是一样的。这是我的问题的真正核心,如果看起来一切(shell、环境变量等)都相同,为什么脚本会在命令行成功的地方失败?
  • 是的,bash -posix 失败。我回过头来弄清楚为什么脚本以符合 POSIX 的模式运行,并为原始问题添加了完整的解释(此处不适合)。
  • 也许还可以解释在当前进程中获取文件意味着获取的文件可以操纵您的环境。因此,如果您运行. B 并且B 执行foo=bar 则此变量将在B 完成时设置,直到当前脚本或交互式shell 退出,而./A 无法更改您的变量(子进程无法更改其父母)。
猜你喜欢
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-14
  • 2018-09-27
  • 1970-01-01
相关资源
最近更新 更多