script -c
나는 진행 상황을 모니터링하고 결과를 이메일로 보낼 수 있도록 명령을 실행하고 출력을 로그 파일에 기록하여 일석이조를 시도하는 bash 스크립트를 가지고 있습니다 .
최종 로그 파일은 모든 진행 상황 출력이 기록되는 터미널 세션에 표시되는 모든 항목의 타이프스크립트이기 때문에 상당히 깁니다. 그러나 read data 를 사용하면 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
전체 원시 데이터가 포함됩니다.
차이점이 있는 이유는 무엇입니까?
cat
디스플레이의 원시 출력이 아닌 디스플레이에서 얻은 것과 동일한 출력을 이메일로 보내고 싶습니다nano
. 그러나cat
원시 데이터는 항상 파일, 다른 명령 등으로 출력됩니다.
아래 명령은 이메일을 통해 원시 데이터를 보냅니다.
> # echo -e "Subject : report" "\n\n\n `cat logfile.log`" | sendmail [email protected]
- TypeScript 파일에서 모든 원시 데이터를 지울 수 있는 방법이 있습니까? 온라인이나 매뉴얼에서 아무것도 찾지 못했습니다.
추가 댓글
조언과 설명 감사드립니다. 실제로 주석에서 제안한 대로 및 를 사용하여 출력을 정리할 수 있었습니다
.rsync
tr
sed
그러나 프로그램 없이 명령을 실행하거나 -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="")