补充评论

补充评论

我有一个 bash 脚本,它试图通过运行命令并将script -c输出写入日志文件来一石二鸟,以便我可以监控进度,然后通过电子邮件给自己发送结果。

最终的日志文件相当长,因为它是终端会话中显示的所有内容的打字稿;每个进度输出都会被记录。但是,如果我使用 读取数据cat,我只能得到终端中显示的最终输出。

例如: script -c 'rsync -ah --info=progress2 folder1 folder2' logfile.log

使用以下命令打开文件nano

> # nano logfile.log
Script started on 2021-07-20 14:22:40+0800
^M         36.84M   0%   34.31GB/s    0:00:00 (xfr#1, to-chk=606/673)^M        808.26M   7%  752.75GB/s    0:00:00 (xfr#31, to-chk=603/673)^M        860.63M   7%  801.52GB/s    0:00:00 (xfr#34, to-chk=592/673)$

Script done on 2021-07-20 14:22:40+0800

鉴于,与cat

> # cat logfile.log                                                                                                                                                                                              
Script started on 2021-07-20 14:22:40+0800
         11.48G 100% 10693.06GB/s    0:00:00 (xfr#616, to-chk=0/673)

Script done on 2021-07-20 14:22:40+0800

但是,如果将cat输出写入文件:

> # cat logfile.log > temp.log

结果temp.log将包括整个原始数据。


  1. 差异的原因是什么?

  2. 我想通过电子邮件发送与我从cat显示器上获得的相同的输出;不是显示的原始输出nano。但是,cat始终将原始数据输出到文件、另一个命令等。

下面的命令通过电子邮件发送原始数据。

> # echo -e "Subject : report" "\n\n\n `cat logfile.log`" | sendmail [email protected]
  1. 有什么办法可以清除打字稿文件中的所有原始数据吗?我在网上或手册中没有找到任何内容。

补充评论

感谢您的建议和解释。事实上,我可以按照评论中的建议清理with和
的输出。rsynctrsed

但是,如果我在没有程序的情况下运行命令-v或运行其他程序(例如)restic,日志将在日志中显示大量擦除序列。

^[[2K[13:34] 487210 files 165.864 GiB, total 503917 files 230.290 GiB, 0 errors

^[[2K[13:34] 487218 files 165.972 GiB, total 503960 files 230.477 GiB, 0 errors

^[[2KFiles:         176 new,     3 changed, 633544 unmodified

^[[2K

^[[2KDirs:          260 new,   140 changed, 106144 unmodified

^[[2K

^[[2KAdded to the repo: 363.231 MiB

^[[2K

^[[2K

^[[2K

^[[2Kprocessed 633723 files, 535.105 GiB in 14:34

^[[2K

^[[2Ksnapshot 9394ca81 saved

^[[2K

^[[2K

^[[1A
Script done on 2021-07-20 00:06:12+0800

我可以设法删除那些带有sed.

如果有一种方法可以将终端的最后输出直接写入cat文件或通过管道传输到文件,那么它可能是更干净、更通用的解决方案sponge

答案1

好的,这是一个(相对)简单的 Python 脚本,它尝试进行此清理。我称之为clean-typescript.py。它当然可以改进,并且可能包含错误,但这是我可以在短时间内想出的。

#!/usr/bin/env python3
# Takes raw terminal output of a program containing control sequences that
# overwrite parts of the output, and attempts to transform it to just the end
# result, writing its output on stdout.
#
# Assumptions/limitations:
#  * Assumes Unix line endings.
#  * Assumes input text is left-to-right, in system character encoding.
#  * Does not attempt to deal with most of the complexities of Unicode.
#  * Does not attempt to interpret every ANSI escape sequence; just the common
#    ones that affect cursor position.
#  * Ignores ANSI SGR (bold/color/etc.) sequences.
#  * Assumes 8-column tab stops.
#  * Assumes the terminal displays an unlimited number of lines.
#  * Ignores absolute positioning sequences (except CHA): this is not for
#    ncurses output and such.
#  * Will not allow the cursor to go up beyond the first line in the file.
#
# Usage: clean-typescript.py FILE COLS
# FILE is the name of the file to read; if omitted or "-", read stdin.
# COLS is how many columns wide the terminal is; if omitted, set to 80.

import sys
from array import array
from enum import Enum

if len(sys.argv) >= 2 and sys.argv[1] != "-":
    f = open(sys.argv[1], "r")
else:
    f = sys.stdin

if len(sys.argv) >= 3:
    cols = int(sys.argv[2])
else:
    cols = 80

lines = [array("u", (" ",)*cols)]
curline = curcol = 0
eol = False

class Dir (Enum):
    UP = 0
    DOWN = 1
    RIGHT = 2
    LEFT = 3

def move_cursor (dir: Dir, count: int):
    global curline, curcol, eol
    if dir == Dir.UP:
        pos = curline - count
        curline = pos if pos >= 0 else 0
    elif dir == Dir.DOWN:
        pos = curline + count
        curline = pos if pos < len(lines) else len(lines) - 1
    elif dir == Dir.RIGHT:
        pos = curcol + count
        curcol = pos if pos < cols else cols - 1
    elif dir == Dir.LEFT:
        eol = False
        pos = curcol - count
        curcol = pos if pos >= 0 else 0

def skip_osc ():
    c = f.read(1)
    while True:
        if c == "\x07":
            return f.read(1)
        if c == "\x1b":
            if f.read(1) == "\\":
                return f.read(1)
        c = f.read(1)

def interpret_seq ():
    c = f.read(1)
    if c == "]": # OSC
        return skip_osc()
    if c != "[": # CSI
        # Other Fe seqs. not supported
        return f.read(1)

    parms = []
    c = f.read(1)
    while True:
        p = ""
        while c >= "0" and c <= "9":
            p += c
            c = f.read(1)
        if p:
            parms.append(int(p))

        if c != ";": break
        c = f.read(1)

    if c == "A":   # CUU
        move_cursor(Dir.UP, parms[0] if len(parms) > 0 else 1)
    elif c == "B": # CUD
        move_cursor(Dir.DOWN, parms[0] if len(parms) > 0 else 1)
    elif c == "C": # CUF
        move_cursor(Dir.RIGHT, parms[0] if len(parms) > 0 else 1)
    elif c == "D": # CUB
        move_cursor(Dir.LEFT, parms[0] if len(parms) > 0 else 1)
    elif c == "E": # CNL
        move_cursor(Dir.LEFT, cols)
        move_cursor(Dir.DOWN, parms[0] if len(parms) > 0 else 1)
    elif c == "F": # CPL
        move_cursor(Dir.LEFT, cols)
        move_cursor(Dir.UP, parms[0] if len(parms) > 0 else 1)
    elif c == "G": # CHA
        move_cursor(Dir.LEFT, cols)
        move_cursor(Dir.RIGHT, parms[0] - 1 if len(parms) > 0 else 0)
    # CUP and ED not implemented
    elif c == "K": # EL
        if (len(parms) == 0 or parms[0] == 0) and not eol:
            for i in range(curcol, cols):
                lines[curline][i] = " "
        elif parms[0] == 1:
            for i in range(0, curcol):
                lines[curline][i] = " "
            if eol:
                append_line()
                move_cursor(Dir.LEFT, cols)
                move_cursor(Dir.DOWN, 1)
        elif parms[0] == 2:
            for i in range(0, cols):
                lines[curline][i] = " "
            if eol:
                append_line()
                move_cursor(Dir.LEFT, cols)
                move_cursor(Dir.DOWN, 1)
    # ED, SU, SD, and HVP also not implemented

    c = f.read(1)
    return c

def append_line ():
    lines.append(array("u", (" ",)*cols))

c = f.read(1)
while c:
    if c == "\x08":   # BS
        if eol:
            eol = False
        else:
            move_cursor(Dir.LEFT, 1)
    elif c == "\x09": # HT
        curcol = (curcol + 8)//8*8
        if curcol >= cols: curcol = cols - 1
    elif c == "\x0a": # LF (implies CR in Unix)
        eol = False
        if curline == len(lines) - 1: append_line()
        move_cursor(Dir.LEFT, cols)
        move_cursor(Dir.DOWN, 1)
    elif c == "\x0b" or c == "\x0c": # VT/FF: just go down one line
        if curline == len(lines) - 1: append_line()
        move_cursor(Dir.DOWN, 1)
    elif c == "\x0d": # CR (stays on same line)
        eol = False
        move_cursor(Dir.LEFT, cols)
    elif c == "\x1b": # Escape seq.
        c = interpret_seq()
        continue
    elif (c >= "\x20" and c <= "\x7e") or c >= "\xa1":
        if not eol: lines[curline][curcol] = c
        if curcol == cols - 1:
            if eol:
                if curline == len(lines) - 1: append_line()
                move_cursor(Dir.LEFT, cols)
                move_cursor(Dir.DOWN, 1)
                lines[curline][curcol] = c
                move_cursor(Dir.RIGHT, 1)
            else:
                eol = True
        else:
            move_cursor(Dir.RIGHT, 1)

    c = f.read(1)

# Final result
print(*("".join(line).rstrip() for line in lines), sep="\n", end="")

相关内容