我有一个脚本应该获取两个目录的文件列表,获取差异并为某些文件执行一些代码。
这些是获取文件列表的命令:
list_in=$(find input/ -maxdepth 1 - type f | sed 's/input\///' | sort -u);
list_out=$(find output/ -maxdepth 1 - type f | sed 's/output\///' | sort -u);
我在正确的目录中执行脚本,所以这应该不会失败。未处理的文件由下式确定
list_todo=$(comm -23 <(echo "$list_in") <(echo "$list_out"));
由于选项-23
forcomm
仅打印第一个参数的行,该行不会出现在两个参数中,并且不会打印唯一出现在第二个参数中的行。
但是,偶尔我会收到错误消息
command substitution: line 3: syntax error near unexpected token `('
command substitution: line 3: `comm -23 <(echo "$list_in") <(echo "$list_out")'
这真的让我很困惑,因为完全相同的脚本在过去三周内运行良好。我在集群上使用它,因此多个进程可能会同时执行该脚本。错误可能是由这个引起的吗?
更新。该脚本是通过调用的./script
,并且我之前显然已设置过chmod +x script
。
(免责声明:即使我正在集群上工作,并且我的脚本的前三行不包含任何锁定机制:当然,没有文件会被处理两次)
答案1
内核识别可以本机执行的某些文件格式。这包括至少一种二进制格式。此外,以#!
(舍邦) 被视为脚本;例如,如果文件位于/path/to/script
并以 开头,那么当您调用 时,#!/bin/bash
内核就会执行。/bin/bash /path/to/script arg1 arg2
/path/to/script arg1 arg2
如果内核无法识别该文件格式,则会从该文件返回 ENOEXEC(执行格式错误)execve
系统调用。遵循古代 Unix 内核没有 shebang 功能的传统,大多数程序在第一次尝试失败并出现错误 ENOEXEC 时会再次尝试执行程序:它们尝试/bin/sh
作为脚本解释器执行。
Bash 是一个值得注意的例外:它在自身中运行脚本,而不是在/bin/sh
.因此,当您从 bash 调用脚本时,您的脚本意外地工作了。
如果您省略 shebang 行,您的脚本可能会在/bin/bash
或 下执行/bin/sh
,具体取决于执行它的程序。并且/bin/sh
显然不支持您的系统上的进程替换。它可能仍然是 bash (错误消息看起来像来自 bash 的错误消息),但是当在名称下调用 bash 时sh
,它会进入不支持进程替换的 POSIX 兼容模式。
这个故事的寓意是:如果你写一个 bash 脚本,你必须把它放在#!/bin/bash
第一行。
答案2
$() 和 <() 构造仅适用于 bash。这意味着您需要将 shebang 行设置为:
#!/bin/bash
特别是这将不是工作:
#!/bin/sh
如果你需要它来工作,sh
那么你可以使用 ` 而不是 $():
list_in=`find input/ -maxdepth 1 - type f | sed 's/input\///' | sort -u`
您可以使用 mkfifo 代替 <()。