如何在不关闭管道的情况下截断 bash 变量赋值

如何在不关闭管道的情况下截断 bash 变量赋值

我正在寻找一种方法来限制从子进程的输出初始化的 Bash 变量中存储的数据量。

这个潜在解决方案的问题在于它在截断之前存储了整个输出。

#!/bin/bash

COMMAND_TO_RUN="du /"

OUT_DATA=$($COMMAND_TO_RUN)
OUT_RESULT=$?

if [[ $OUT_RESULT -ne 0 ]]; then
    echo "${OUT_DATA:0:10000}" | head mail -s "Command failed" [email protected]
fi

当读取前 10k 个输出时,替代方案OUT_DATA=$($COMMAND_TO_RUN | head -c 10000)会取消该命令,并且我需要该命令运行完成以捕获其退出状态。

我可以很容易地在 中完成此操作python,但我希望有一个bash唯一的解决方案。并且无需诉诸写入磁盘。

答案1

使用函数。为了便于阅读,下面的示例对命令等进行了硬编码。

truncated_du() {
   du / | { head -c 10000; cat >/dev/null; }
   return "${PIPESTATUS[0]}"
}

out_data="$(truncated_du)"
out_result="$?"

head最多将所需数量的数据传递到函数的标准输出。如果还有更多数据,cat则会丢弃它而不中断。函数从数组中检索并返回的du退出状态。duPIPESTATUS

注意:我的变量使用lower case;参见这个答案


如果没有函数,相同的解决方案如下所示:

out_data="$(
   du / | { head -c 10000; cat >/dev/null; }
   exit "${PIPESTATUS[0]}"
)"
out_result="$?"

我认为这种方式的可读性较差。

答案2

这会限制您捕获的数量,但我假设它以read退出状态退出(我不知道它是否会停止,它会像head不尊重退出状态那样停止命令:`

IFS= read -r -d '' -n $chars_to_capture foo < <($command)

它似乎让命令运行完成,但是您没有获得命令的退出状态,您获得了read没有帮助的退出状态。

一个例子

$ IFS= read -r -d '' -n 19 foo < <(seq 50; touch somefile; exit 42); echo "exit=$?"; ls -l somefile
exit=0
-rw-r--r-- 1 glennjackman staff 0 Jul 22 22:04 somefile

我认为文件被触碰这一事实意味着在捕获到请求的字符数后命令不会被中断。

也许你可以将退出状态写入子 shell 中的文件:

IFS= read -r -d '' -n 10000 out_data < <( $command_to_run; echo $? > statusfile )
if [[ -f statusfile ]] && [[ "$(<statusfile)" != "0" ]]; then
    printf "%s\n" "$out_data" | head mail -s "Command failed" [email protected]
    rm statusfile
fi

相关内容