Answers:
您不能随便在#!
一行上放置多个参数。那意味着只有一个完整的路径和一个参数(例如#!/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/
说明:
b
; 的函数;只要函数的语法格式正确(特别是您不能有空函数),内容就无关紧要。然后,如果为true(即始终),则执行sed
对脚本。()
标签,然后输入格式正确的输入。然后是一条i
命令,该命令无效,因为它总是被跳过。最后,()
标签后面是脚本的有用部分。#!/usr/bin/env
没有争论。” 措辞不太好。也许“或者#!/usr/bin/env sed
没有对sed的争论”。
从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
特有的功能。
此答案提供了一种优雅的解决方案的路径:https : //stackoverflow.com/a/1655389/642372
read
将Heredoc转换为Shell变量。sed
。样品:
#!/usr/bin/env bash
read -rd '' SED_SCRIPT <<EOD
# Your sed script goes here.
s/a/b/
EOD
exec sed "$SED_SCRIPT" "$@"
我喜欢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///
替代品的额外优势。