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

Che*_*eng 31 scripting sed executable env

输入/usr/bin/env sed -f终端工作。

但如果将其用作shebang,

#!/usr/bin/env sed -f 
s/a/b/
Run Code Online (Sandbox Code Playgroud)

脚本将无法执行:

/usr/bin/env: sed -f: No such file or directory
Run Code Online (Sandbox Code Playgroud)

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

Gil*_*il' 35

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

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

#!/bin/sh
exec sed '
s/a/b/
' "$@"
Run Code Online (Sandbox Code Playgroud)

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

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF
Run Code Online (Sandbox Code Playgroud)

可以通过有用地使用cat. 由于这会将整个脚本再次放在命令行上,因此它不符合 POSIX 标准,但在实践中很大程度上是可移植的。

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

另一种解决方法是编写一个脚本,该脚本可以同时被 sh 和 sed 解析。这是便携式的,相当高效,只是有点丑陋。

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

说明:

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


小智 9

从 GNU coreutils v8.30 开始,您可以执行以下操作:

#!/usr/bin/env -S sed -f
Run Code Online (Sandbox Code Playgroud)

在最近的(2018年4月20日)中加入此功能提交env.c在GNU 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.
Run Code Online (Sandbox Code Playgroud)

GNU coreutils 手册中提供了更多示例。

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

my_sed_script.sed

#!/usr/bin/env -vS sed -f
s/a/b/
Run Code Online (Sandbox Code Playgroud)

执行:

$ ./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’
Run Code Online (Sandbox Code Playgroud)

注意:这仅适用于使用 的shebang /usr/bin/env,这 --split-string是 GNU 的一个特性env


jll*_*gre 7

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


小智 2

env 正在尝试查找名为“sed -f”的文件。您可以尝试“#!/usr/bin/sed -f”作为您的 shebang 行。