我执行以下操作:
$ ./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}
变成ab
and )。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
) 结尾,不需要引号来表示它们。