我的意思是使用以下形式的GNUenv
或 BSD命令:env
env [name=value ...] [utility [args ...]]
看起来没有办法value
部分转义特殊字符,但我不太确定如何env
实现解析该value
部分。
我知道有很多方法可以通过 shell 的功能来做到这一点,但我想在没有 shell 帮助的情况下传递文字字符串(有点像execute env
by exec
)。也就是说,我需要找到某种带有换行符并受env
命令支持的文字字符串格式。例如:
env FOO=LITERAL_STRING ruby -e 'puts ENV["FOO"]'
这里LITERAL_STRING
应该包含一个带有换行符的文字字符串并且env
应该理解该格式。
使用上述命令,预期输出应该是:
hello
world
我想知道是否可能。我将不胜感激你的帮助。
环境
env
我使用 BSD
env
,所以它无法打印版本。不知道是否man
可以帮助:$ man env | tail -n1 BSD April 17, 2008 BSD
操作系统
$ sw_vers ProductName: macOS ProductVersion: 11.6 BuildVersion: 20G165
答案1
你的理解是不正确的。当您将内容存储"hello\nworld"
到变量中时,将按\n
字面解释。仅当您调用诸如printf
或echo
with -e
flag 之类的工具时,它们才会在打印到控制台时扩展这些反斜杠序列。
在您的情况下,您希望将变量传递到环境并扩展换行符,建议使用ANSI C 风格的$'...'
引用运算符ksh93 的版本,现在已得到其他几个 shell 的支持,包括 bash 和 zsh:
env FOO=$'hello\nworld' ruby -e 'puts ENV["FOO"]'
或者,如果可移植性是一个问题,一个简单的解决方案是将这些换行符放在您想要的位置
f="hello
world"
env FOO="$f" ruby -e 'puts ENV["FOO"]'
答案2
答案3
该env
命令在执行时会从系统调用中获取许多参数(就像任何命令一样)execve()
。执行它的进程将执行以下操作:
execve("/path/to/env", ["env", "-options", "var=value", "cmd", "arg"], environ);
并且env
依次执行(通常在同一过程中):
execve("/path/to/cmd", ["cmd", "arg"], modified_environ);
其中modified_environ
isenviron
但var=value
附加到它,或者如果已经有一个或多个以var=
in开头的字符串environ
,则其中第一个(至少)替换为var=value
。
这些参数是以 NUL 结尾的字节数组,因此它们唯一不能包含的字节是 NUL 字节。这同样适用于名称和值都不能包含 NUL 字节的环境变量。
env
启动时,从头到尾处理其参数。
开头的那些以 开头的-
将被视为选项。
-
单独也当作古形式来-i
忽略environ
。
选项后面的那些--
至少包含一个的选项(或可用于标记选项结尾的选项)=
将被视为环境变量字符串(放在modified_environ
上面)。第一个不包含字符的非选项参数=
被视为命令名称(它将作为第一个参数传递给命令,也用于查找 中可执行文件的路径$PATH
)。
此后的任何参数都将作为参数传递给命令,即使它们以字符开头-
或包含=
字符。
因此对于env
其本身而言,只有-
和=
字符是特殊的;-
仅在选项处理期间,并且=
仅在找到命令名称之前。
为了能够传递以 开头的环境变量名称-
或以 开头的命令名称-
,您可以使用--
:
execve("/usr/bin/env", ["env", "--", "-var-=value", "cmd"], environ);
execve("/usr/bin/env", ["env", "--", "-cmd-"], environ);
对于几种env
实现方式,-
单独的以下--
仍然不会被视为命令名称。因此,如果您想调用名为 的命令-
,则需要事先传递至少一个环境变量:
execve("/usr/bin/env", ["env", "--", "dummy=", "-", "args"], environ);
(或使用/path/to/-
or./-
代替-
并绕过该命令的路径$PATH
查找-
)。
env
但是,不允许您运行名称包含=
字符的命令,并且无法=
传递modified_environ
.=
在这种情况下,没有办法逃脱这个角色。
但就您的情况而言,除了 NUL 字节之外,env
FOO=LITERAL_STRING
没有任何字符本身会成为问题。LITERAL_STRING
env
现在,当用某种语言编写代码来执行该env
命令时,该语言中可能存在无法按字面输入的字符。
例如,在 中perl
,您可以这样做:
exec "env", "--", "-text-=hello\nworld\n", "printenv", "--", "-text-";
将换行符表示为\n
双引号内,但您也可以这样做:
exec qw(env --), q(-text-=hello
world
), qw(printenv -- -text-);
传递包含换行符的字符串。
在类似 Bourne 的 shell 语言的语法中,换行符在单引号或双引号内时并不特殊(例如 的q(...)
或qq(...)
引用运算符perl
),因此您可以这样做:
exec env -- "-text-=hello
world
" printenv -- -text
在 C shell 中,或者在 rc shell 中,情况会有所不同,其中"..."
不是引用运算符。
你的env FOO=LITERAL_STRING ruby -e 'puts ENV["FOO"]'
代码看起来像 shell 语言中的代码。该语法对于大多数 shell 都是有效的,因为几乎所有 shell 都将空格理解为单词分隔符和'...'
引用运算符。
当您exec
在开始时省略 时,shell 会在子进程中运行该命令,然后等待该进程终止或挂起。
如果这env FOO=LITERAL_STRING ruby -e 'puts ENV["FOO"]'
是一种语言,不允许按字面输入换行符(可能不是 shell),也不允许使用某种形式的编码(\n
, %0a
,
...),但显然支持'...'
像大多数 shell 一样的引用运算符,除了 BSDenv -S
已经把戏了由 @UncleBilly 建议,您可以调用解释器或可以执行命令并允许以某种编码方式指定换行符的语言,例如perl
, ruby
, python
, ksh93
, zsh
, bash
...
既然你已经有了ruby
:
ruby -e 'exec "env", "FOO=a\n\n\nb", *ARGV' -- ruby -e 'puts ENV["FOO"]'
但是任何血统编程语言都ruby
可以自行设置环境变量,因此您不需要env
:
ruby -e 'ENV["FOO"]="a\n\n\nb"; exec *ARGV' -- ruby -e 'puts ENV["FOO"]'
答案4
在值中使用文字换行符:
$ env FOO="hello
world" ruby -e 'puts ENV["FOO"]'
输出:
hello
world
如果您想将一个字符串\n
转换为实际的换行符,请使用诸如 之类的实用程序printf
,就像在其他地方一样:
$ env FOO="$(printf "%b" 'hello\nworld')" ruby -e 'puts ENV["FOO"]'
hello
world
env
在这方面并不特殊(事实上,env
只看到真正的换行符,因为它在进程开始之前就已扩展)。