在标准输出中显示命令输出,然后通过转换保存到文件?

时间:2018-07-18 02:50:01

标签: bash

我有一个长期运行的命令,该命令会定期输出。让我们假设它是:

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

如何在不等待命令退出然后立即读取文件的情况下即时进行此转换?

3 个答案:

答案 0 :(得分:1)

请注意,在您的原始代码中,{1..$i}是一个错误,因为序列不能包含变量。我已将其替换为seq。另外,内部for循环缺少dodone

无论如何,我会使用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
}