我有一个长期运行的命令,该命令会定期输出。让我们假设它是:
function my_cmd()
{
for i in {1..9}; do
echo -n $i
for j in {1..$i}
echo -n " "
echo $i
sleep 1
done
}
输出将是:
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
我想在同时显示命令输出的同时将其保存到文件中。
这可以通过my_cmd | tee -a res.txt
完成。
现在,我想按原样显示输出到终端,但是以转换后的样式保存到文件中,例如用sed "s/ //g"
。
因此res.txt变为:
11
22
33
44
66
77
88
99
如何在不等待命令退出然后立即读取文件的情况下即时进行此转换?
答案 0 :(得分:1)
请注意,在您的原始代码中,{1..$i}
是一个错误,因为序列不能包含变量。我已将其替换为seq
。另外,内部for循环缺少do
和done
。
无论如何,我会使用process substitution。
#!/usr/bin/env bash
function my_cmd {
for i in {1..9}; do
printf '%d' "$i"
for j in $(seq 1 $i); do
printf ' '
done
printf '%d\n' "$j"
sleep 1
done
}
my_cmd | tee >(tr -d ' ' >> res.txt)
进程替换通常会使bash在/dev/fd
中创建一个条目,该条目被馈送到相关命令。替换的内容异步运行,因此不会阻止向其发送数据的过程。
请注意,进程替换不是REAL文件,因此-a
的{{1}}选项是没有意义的。如果您确实要附加到输出文件,则在替换中使用tee
是可行的方法。
如果您不喜欢进程替换,另一种选择是重定向到备用文件描述符。例如,您可以使用:
代替上面脚本中的最后一行。>>
这将创建文件描述符exec 5>&1
my_cmd | tee /dev/fd/5 | tr -d ' ' > res.txt
exec 5>&-
,该文件描述符将重定向到您的 real 标准输出终端。然后,它告诉/dev/fd/5
对此进行写操作,从而允许tee
的普通stdout在最终重定向到日志文件之前由其他管道元素处理。
您选择的方法由您决定。我发现流程替换更清晰。
答案 1 :(得分:0)
您需要在函数中进行某些修改。您可以在for循环中使用tee
来同时打印和写入文件。以下脚本可能会得到您想要的结果。
#!/bin/bash
filename="a.txt"
[ -f $filename ] && rm $filename
for i in {1..9}; do
echo -n $i | tee -a $filename
for((j=1;j<=$i;j++)); do
echo -n " "
done
echo $i | tee -a $filename
sleep 1
done
答案 2 :(得分:0)
我将使用printf
及其格式化功能%Xs
来代替空白循环。
此外,我将使用双重打印(用于stdout和您的文件),而不是使用管道并启动新进程。
所以您的函数可能如下所示:
function my_cmd() {
for i in {1..9}; do
printf "%s %${i}s\n" $i $i
printf "%s%s\n" $i $i >> res.txt
done
}