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


Answers:


33

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

一种获得可移植脚本的解决方法是使用#!/bin/sh和Shell包装器,将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

可以通过对有用的使用来弥补Heredoc的不足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对它跳过的部分很挑剔。)

1
“或者#!/usr/bin/env没有争论。” 措辞不太好。也许“或者#!/usr/bin/env sed没有对sed的争论”。
cjm 2011年

+1(表示“有点丑陋”)和双语言脚本:)
溯源

6

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


2

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


2

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

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

在最近的(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.

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特有的功能。


1

此答案提供了一种优雅的解决方案的路径:https : //stackoverflow.com/a/1655389/642372

  1. read 将Heredoc转换为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" "$@"

1

我喜欢Walker的解决方案,但是可以改进(将其放在单独的答案中,因为注释不接受预格式化的文本)。这可能不适用于所有版本的Linux或Bash,但是在Ubuntu 17.10上,您可以将其缩减为以下内容:

#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"

您可以删除eval和简化read命令,但必须摆脱Heredoc中的注释。

此外,对于您想了解的有关sed的所有知识,但又不敢问,在http://www.grymoire.com/unix/sed.html上有一个非常有用的教程。这建议一个更好的解决方案,但要以丢失/usr/bin/env和硬编码sed路径为代价:

#!/bin/sed -f
s/a/b/
s/c/d/

这具有支持多个s///替代品的额外优势。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.