AWK:防止传递给外部实用程序的参数进行字段分割

2 shell awk

在 AWK 脚本内部,我可以将变量作为参数传递给外部实用程序:

awk 'BEGIN {
    filename = "path_to_file_without_space"
    "file " filename | getline
    print $0
}'
Run Code Online (Sandbox Code Playgroud)

但如果变量包含空格,

awk 'BEGIN {
    filename = "path to file with spaces"
    "file " filename | getline
    print $0
}'
Run Code Online (Sandbox Code Playgroud)

我收到错误

file: cannot open `path' (No such file or directory)
Run Code Online (Sandbox Code Playgroud)

建议在空格上分割参数,这与 shell 在空格上分割未加引号的变量的方式非常相似。我想通过将 shell 的 IFS 设置为 null 来禁用 shell 字段分割,如下所示

"IFS= file " filename | getline
Run Code Online (Sandbox Code Playgroud)

或者在运行 AWK 命令之前将 IFS 设置为 null,但这两个选项都没有任何区别。如何避免这种字段分裂?

Kus*_*nda 5

您必须引用文件名:

awk 'BEGIN {
    filename = "path to file with spaces"
    "file \"" filename "\"" | getline
    print
}'
Run Code Online (Sandbox Code Playgroud)

或者,正如评论中所建议的,为了便于阅读,

awk 'BEGIN {
    DQ = "\042" # double quote (ASCII octal 42)
    filename = "path to file with spaces"
    "file " DQ filename DQ | getline
    print
}'
Run Code Online (Sandbox Code Playgroud)

或者,假设这是一个更大awk程序的一部分,

BEGIN {
    SQ = "\047"
    DQ = "\042"
}

BEGIN {
    name = "filename with spaces"
    cmd = sprintf("file %s%s%s", DQ, name, DQ)

    cmd | getline
    close(cmd)

    print
}
Run Code Online (Sandbox Code Playgroud)

也就是说,完成后关闭该命令以保存打开的文件句柄。在单独的块中设置方便的“常量” BEGIN(这些块按顺序执行)。sprintf使用到单独的变量中创建命令。(这些东西中的大多数显然是针对更长或更复杂的awk程序,需要提供一种可读的结构以便维护;人们也可以想象编写一个引用字符串的dquote()andsquote()函数)

“管道”的左侧将计算为文字字符串

file "path to file with spaces"
Run Code Online (Sandbox Code Playgroud)

基本上, usingcmd | getline使awk调用sh -c带有单个参数,即 string cmd。因此,必须正确引用该字符串才能使用 执行sh -c

技术细节可在POSIX 标准中找到:

expression | getline [var]

从命令输出通过管道传输的流中读取输入记录。如果当前没有打开流,则应创建该流并将 的值expression作为其命令名称。创建的流应等效于通过调用函数创建的流,popen()其中表达式的值作为命令参数,值r作为参数mode。只要流保持打开状态,expression计算结果为相同字符串值的后续调用就应从流中读取后续记录。流应保持打开状态,直到close使用计算结果为相同字符串值的表达式调用函数为止。那时,流将被关闭,就像通过调用该pclose()函数一样。如果var省略,$0则应NF设置;否则,var应设置,并且如果合适,应将其视为数字字符串(请参阅 awk 中的表达式)。

这里指的函数popen()是Cpopen()库函数。这安排了给定的字符串由 执行sh -c

system()如果使用带空格的文件名执行命令,您将遇到完全相同的问题,但在这种情况下system(),将调用 C 库的函数,该函数sh -c以类似的方式进行调用popen()(但 I/O 流的管道不同)。

因此,如果使用单个参数调用,任何设置都IFS无济于事sh -c

file path to file with spaces
Run Code Online (Sandbox Code Playgroud)