如何将整个 stdin 逐字转换为命令行参数?

Vi.*_*Vi. 2 command-line bash shell-script stdin

尝试 1

xargs -I '{}' -0 -n 1 myprogram --arg '{}' --other-options
Run Code Online (Sandbox Code Playgroud)

但这不会保留零字节。此外,该程序可能会运行多次。但是,它不会在零字节进入标准输入的情况下失败,而是多次运行程序。

尝试 2

myprogram --arg "`cat`" --other-options
Run Code Online (Sandbox Code Playgroud)

但这不会保留尾随空格。

尝试 3

bash -c 'read -r -d "" INPUT ; myprogram --arg "$INPUT" --other-options'
Run Code Online (Sandbox Code Playgroud)

似乎与终端混淆,也无法保留尾随空格。


我如何正确,可靠,可读,兼容地做到这一点?

Cel*_*ada 6

在命令行参数中不可能有 NUL 字节,所以问题是如果标准输入中NUL 字节,你想发生什么。

正如您所指出的,在这种情况下,您的候选解决方案 #1 会多次运行该命令。这并不理想。但是没有理想的解决方案可以让您处理真正的二进制输入。在我看来,您在这里唯一的其他合理选择是:

  • 删除 NUL 字节并继续
    • tr -d '\0' |之前插入xargs
  • 将 NUL 字节转换为其他内容并继续
    • tr '\0' something-else |之前插入xargs(如果something-else是单字节)
  • 如果有 NUL 字节,则中止和保释

    • 使用 bash 或 ksh93(除非输入在末尾包含单个空字节,在这种情况下它会被静默删除):

      { read -r -d '' input;
        if [ "$(wc -c)" = 0 ]; then
          printf %s "$input" | xargs …;
        else
          echo 1>&2 "Null bytes detected, bailing out"
          exit 2
        fi
      }
      
      Run Code Online (Sandbox Code Playgroud)
    • 使用 zsh(而不是其他 shell,如 bash、ksh 或 dash):

      input=$(<&0)
        if [[ $input != *$'\0'* ]]; then
          printf %s "$input" | xargs …;
        else
          echo 1>&2 "Null bytes detected, bailing out"
          exit 2
        fi
      
      Run Code Online (Sandbox Code Playgroud)
    • 或者使用临时文件。
  • 在第一个 NUL 字节之后截断输入

    • tr '\0\n' '\n\0' | head -n 1 | tr '\0\n' '\n\0'之前插入xargs(假设您head是空安全的)