将生成的空字符串作为命令行参数传递

将生成的空字符串作为命令行参数传递

我执行以下操作:

$ ./input ""

输出:

argc 2
argv[1][0] 0

但是如果我想以基于程序的方式传递(几个)空引号:

$ python -c 'print "\"\""'
""

./input $(python -c 'print "\"\""')

给出:

argc 2
argv[1][0] 22 - (22 hex value for ")

那么我怎样才能生成类似的东西:

$ ./input "" "" "" ""

并得到与示例 1 相同的结果?

答案1

./input $(cmd)

因为,$(cmd)未加引号,这是一个 split+glob 运算符。 shell 检索 的输出cmd,删除所有尾随换行符,然后根据$IFS特殊参数的值将其拆分,然后执行文件名生成(例如变成*.txt当前目录中非隐藏 txt 文件的列表)生成的单词(后一部分不带有zsh),并且在 的情况下ksh也执行大括号扩展(例如a{b,c}变成aband )。ac

的默认值$IFS包含 SPC、TAB 和 NL 字符(还有 中的 NUL zsh,其他 shell 要么删除 NUL,要么扼杀它们)。这些(不是 NUL)也恰好是 IFS 空白字符,在 IFS 分割时会被特殊对待。

如果 的输出cmd" a b\nc \n",则 split+glob 运算符将生成"a","b""c"的参数./input。对于 IFS 空白字符,不可能split+glob生成空参数,因为一个或多个 IFS 空白字符的序列被视为分隔符。要生成空参数,您需要选择一个不是 IFS 空白字符的分隔符。实际上,任何非空白字符都可以(最好也避免使用并非所有 shell 都支持的多字节字符)。

例如,如果您这样做:

IFS=:          # split on ":" which is not an IFS-whitespace character
set -o noglob  # disable globbing (also brace expansion in ksh)
./input $(cmd)

如果cmd输出a::b\n,那么 split+glob 运算符将产生"a","""b"参数(请注意,"s 不是值的一部分,我只是在这里使用它们来帮助显示值)。

对于a:b:\n,根据 shell,这将导致"a"and"b""a", "b"and ""。您可以使用以下命令使其在所有 shell 中保持一致

./input $(cmd)""

(这也意味着对于空输出cmd(或仅包含换行符的输出),./input将接收一个空参数而不是根本没有参数)。

例子:

cmd() {
  printf 'a b:: c\n'
}
input() {
  printf 'I got %d arguments:\n' "$#"
  [ "$#" -eq 0 ] || printf ' - <%s>\n' "$@"
}
IFS=:
set -o noglob
input $(cmd)

给出:

I got 3 arguments:
 - <a b>
 - <>
 - < c>

另请注意,当您这样做时:

./input ""

这些"是 shell 语法的一部分,它们是 shell 引用运算符。这些"字符不会传递给input.


^IFS 空白字符,每个 POSIX 是在语言环境中分类的字符[:space:],并且恰好$IFS在 ksh88(POSIX 规范所基于的)和大多数 shell 中,这仍然限于 SPC、TAB 和 NL。我发现在这方面唯一符合 POSIX 标准的 shell 是yash.ksh93并且bash(自 5.0 起)还包括其他空格(例如 CR、FF、VT...),但仅限于单字节空格(请注意某些系统(如 Solaris),其中包括单字节的不间断空格在某些地区)

答案2

您可以以编程方式生成整个命令行,然后复制粘贴它,或者通过 eval 运行它,例如:

$ perl -e 'printf "./args.sh %s\n", q/"" / x 10' 
./args.sh "" "" "" "" "" "" "" "" "" "" 

$ eval "$(perl -e 'printf "./args.sh %s\n", q/"" / x 100')"
$#: 100
$1: ><

q/"" /是 Perl 引用字符串的方式之一,x 100制作数百个副本并将它们连接起来。)

eval将其参数作为 shell 命令处理,运行所有引用处理和扩展。这意味着,如果任何输入来自不受信任的来源,您在生成评估代码时需要小心以防止漏洞。

如果你想要空参数变量的数量,那应该是没有问题的(至少我无法想出 Perl 的第二个操作数如何x被误用,因为它将操作数折叠为整数):

$ n=33
$ eval "$(perl -e 'printf "./args.sh %s\n", q/"" / x $ARGV[0]' "$n")"
$#: 33
$1: ><

答案3

但实际上你想通过什么?空的引号或空字符串?两者都是有效的参数,这个简单的 bash 脚本可以帮助说明这一点:

#!/bin/bash

printf "Argument count: %s.\n" "${#@}"

它只是打印传递给它的参数数量。s为了简洁起见,我称之为。

$ ./s a
Argument count: 1.
$ ./s a b
Argument count: 2.
$ ./s a b ""
Argument count: 3.
$ ./s a b "" ""
Argument count: 4.
$ ./s a b "" "" \"\"
Argument count: 5.

正如您所看到的,空字符串只是空字符串 - 引号在解析时被删除 - 并且它们仍然是有效的参数。 shell 将它们输入到命令中。但""也可以传承。但它不是一个空字符串。它包含两个字符。

在底层,对于 C,字符串以 NUL ( \0) 结尾,不需要引号来表示它们。

相关内容