shebang中使用`/usr/bin/env sed -f`的方法?

shebang中使用`/usr/bin/env sed -f`的方法?

/usr/bin/env sed -f在终端中输入有效。

但如果把它当作一个shebang,

#!/usr/bin/env sed -f 
s/a/b/

该脚本将无法执行:

/usr/bin/env: sed -f: No such file or directory

我有点相信它与 -f 有关。但如何解决这个问题呢?

答案1

#!你不能,可移植地,在一行上放置多个参数。这意味着只有一个完整路径和一个参数(例如#!/bin/sed -f#!/usr/bin/sed -f),或者#!/usr/bin/env没有给解释器的参数。

获取可移植脚本的解决方法是使用#!/bin/shshell 包装器,将 sed 脚本作为命令行参数传递。请注意,这并未受到 POSIX 的认可(为了可移植性,多指令脚本必须为每个指令编写一个单独的-e参数),但它适用于许多实现。

#!/bin/sh
exec sed '
s/a/b/
' "$@"

对于长脚本,使用heredoc可能更方便。 Heredoc 的一个优点是您不需要引用内部的单引号(如果有)。一个主要的缺点是该脚本是通过其标准输入提供给 sed 的,这会带来两个恼人的后果。某些版本的 sed 需要-f /dev/stdin而不是-f -,这对于可移植性来说是一个问题。更糟糕的是,脚本不能充当过滤器,因为标准输入是脚本而不是数据。

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF

此处文档的缺点可以通过有效使用 来弥补cat。由于这会将整个脚本再次置于命令行上,因此它不符合 POSIX 标准,但在实践中很大程度上是可移植的。

#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF

另一个解决方法是编写一个可以由 sh 和 sed 解析的脚本。这是便携式的,相当高效,只是有点难看。

#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/

说明:

  • 在 sh 下:定义一个名为 的函数b;只要函数的语法格式正确(特别是不能有空函数),内容并不重要。然后,如果为 true(即始终),则执行sed脚本。
  • 在 sed 下:分支到()标签,然后是一些格式正确的输入。然后是一个i命令,该命令没有任何效果,因为它总是被跳过。最后是()标签,后面是脚本的有用部分。
  • 在 GNU sed、BusyBox 和 OpenBSD 下测试。 (您可以在 GNU sed 上使用更简单的东西,但 OpenBSD sed 对它跳过的部分很挑剔。)

答案2

从 GNU coreutils 开始v8.30, 你可以做:

#!/usr/bin/env -S sed -f

该功能是最近添加的(2018-04-20)犯罪env.cGNU coreutils 包中,其中添加了-S--split-string选项。

env手册页:

OPTIONS
-S/--split-string usage in scripts
    The  -S  option allows specifing multiple parameters in a script.
    Running a script named 1.pl containing the following first line:

            #!/usr/bin/env -S perl -w -T

    Will execute perl -w -T 1.pl .

    Without the '-S' parameter the script will likely fail with:

            /usr/bin/env: 'perl -w -T': No such file or directory

    See the full documentation for more details.

更多示例可在GNU coreutils 手册

如果您还使用详细-v输出选项,您可以准确地看到如何 env分割参数字符串:

my_sed_script.sed

#!/usr/bin/env -vS sed -f
s/a/b/

执行:

$ ./my_sed_script.sed
split -S:  ‘sed -f’
 into:    ‘sed’
     &    ‘-f’
executing: sed
   arg[0]= ‘sed’
   arg[1]= ‘-f’
   arg[2]= ‘./my_sed_script.sed’

笔记:这仅适用于使用 的 shebang /usr/bin/env,这 --split-string是 GNU 的一项专门功能env

答案3

根据操作系统的不同,shebang (#!) 有各种不兼容的实现。有些正在构建完整的参数列表,有些正在保留命令路径并将所有剩余参数作为单个参数,有些则忽略所有参数并仅传递命令路径,最后,有些将整个字符串作为单个参数传递命令。你似乎属于后一种情况。

答案4

这个答案提供了一条通往优雅解决方案的途径:https://stackoverflow.com/a/1655389/642372

  1. read将定界符放入 shell 变量中。
  2. 将该变量作为位置参数传递给sed.

样本:

#!/usr/bin/env bash

read -rd '' SED_SCRIPT <<EOD      
# Your sed script goes here.
s/a/b/
EOD

exec sed "$SED_SCRIPT" "$@"

相关内容