/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/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
此处文档的缺点可以通过有效使用 来弥补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.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
。
答案3
根据操作系统的不同,shebang (#!) 有各种不兼容的实现。有些正在构建完整的参数列表,有些正在保留命令路径并将所有剩余参数作为单个参数,有些则忽略所有参数并仅传递命令路径,最后,有些将整个字符串作为单个参数传递命令。你似乎属于后一种情况。
答案4
这个答案提供了一条通往优雅解决方案的途径:https://stackoverflow.com/a/1655389/642372
read
将定界符放入 shell 变量中。- 将该变量作为位置参数传递给
sed
.
样本:
#!/usr/bin/env bash
read -rd '' SED_SCRIPT <<EOD
# Your sed script goes here.
s/a/b/
EOD
exec sed "$SED_SCRIPT" "$@"