Loại bỏ màu khỏi đầu ra


140

Tôi có một số tập lệnh tạo đầu ra với màu sắc và tôi cần xóa mã ANSI.

#!/bin/bash

exec > >(tee log)   # redirect the output to a file but keep it on stdout
exec 2>&1

./somescript

Đầu ra là (trong tệp nhật ký):

java (pid  12321) is running...@[60G[@[0;32m  OK  @[0;39m]

Tôi không biết làm thế nào để đặt ký tự ESC ở đây, vì vậy tôi đặt @nó vào vị trí của nó.

Tôi đã thay đổi tập lệnh thành:

#!/bin/bash

exec > >(tee log)   # redirect the output to a file but keep it on stdout
exec 2>&1

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g"

Nhưng bây giờ nó cho tôi (trong tệp nhật ký):

java (pid  12321) is running...@[60G[  OK  ]

Làm thế nào tôi cũng có thể loại bỏ điều này ' @[60G?

Có lẽ có một cách để tắt hoàn toàn màu cho toàn bộ kịch bản?


Đối với nút / npm, bạn có thể sử dụng strip-ansi: github.com/chalk/strip-ansi .
Joshua Pinter

Câu trả lời:


164

Theo Wikipedia , các [m|K]trong sedlệnh bạn đang sử dụng được thiết kế đặc biệt để xử lý m(lệnh màu) và K(các "phần xóa dòng" lệnh). Tập lệnh của bạn đang cố gắng đặt vị trí con trỏ tuyệt đối thành 60 ( ^[[60G) để có được tất cả các OK trong một dòng mà seddòng của bạn không bao gồm.

(Đúng, [m|K]có lẽ nên (m|K)hoặc [mK], bởi vì bạn không cố gắng khớp với một nhân vật ống. Nhưng điều đó không quan trọng ngay bây giờ.)

Nếu bạn chuyển trận đấu cuối cùng trong lệnh của mình sang [mGK]hoặc (m|G|K), bạn sẽ có thể bắt được chuỗi điều khiển bổ sung đó.

./somescript | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g"

29
Người dùng BSD / OSX: Chúng tôi thường không có tùy chọn -r để sed. brew install gnu-sedsẽ cài đặt một phiên bản có khả năng. Chạy với gsed.
Nicolai S

1
Nếu tôi làm echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, tôi nhận được: foo^O bar$Vì vậy, tôi đoán một số ký tự không được loại bỏ chính xác, phải không? Bạn có biết làm thế nào để sửa?
edi9999

1
@ edi9999 Theo như tôi có thể nói, sự khác biệt ở đây là cài đặt màu ngoài 16 màu (dưới dạng setafhỗ trợ) yêu cầu nhiều tham số hơn chỉ hai; regex của tôi hỗ trợ hai. Thay đổi đầu tiên ?cho *nên giúp đỡ. Xử lý sgr0là có thể nhưng dựa trên một tìm kiếm, nó có thể phát triển bên ngoài phạm vi của câu trả lời dựa trên regex hacky này.
Jeff Bowman

Ok, tôi đã thêm một câu trả lời thêm một sedđường ống để loại bỏ ký tự "shift in"
edi9999

7
Điều này không hoạt động đáng tin cậy vì có thể có một giá trị thứ ba (ala [38;5;45m). Câu trả lời thay thế này hoạt động unix.stackexchange.com/a/55547/168277
davemyron

30

Tôi không thể nhận được kết quả tốt từ bất kỳ câu trả lời nào khác, nhưng những điều sau đây có hiệu quả với tôi:

somescript | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g"

Nếu tôi chỉ xóa char điều khiển "^ [", nó sẽ để lại phần còn lại của dữ liệu màu, ví dụ: "33m". Bao gồm mã màu và "m" đã thực hiện thủ thuật. Tôi bối rối với s / \ x1B // g không hoạt động vì \ x1B [31m chắc chắn hoạt động với tiếng vang.


6
Trên OSX (BSD sed), sử dụng -Ethay vì -rcho regex mở rộng. Nhiều hơn có thể được tìm thấy ở đây
Assambar

tôi đã phải thay {1,3}tới {,3}(nếu không nó vẫn bỏ qua một số kiểm soát), nhờ giải pháp của bạn!
bất động

6
Vì chúng có thể là nhiều số được phân tách bằng dấu chấm phẩy (cho màu nền, đậm, in nghiêng, v.v ...). Lệnh này có tác dụng với tôi:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu

Cái này (trong số nhiều cái tôi đã thử) hoạt động với đầu ra Ansible đã được chạy với unbuffer.
Martin

23

IMHO, hầu hết các câu trả lời này đều cố gắng quá mức để hạn chế những gì bên trong mã thoát. Kết quả là, cuối cùng họ thiếu các mã phổ biến như [38;5;60m(tiền cảnh màu ANSI 60 từ chế độ 256 màu).

Họ cũng yêu cầu -rtùy chọn cho phép mở rộng GNU . Đây là không bắt buộc; họ chỉ làm cho regex đọc tốt hơn.

Đây là một câu trả lời đơn giản hơn để xử lý các thoát 256 màu và hoạt động trên các hệ thống không có GNU sed:

./somescript | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g'

Điều này sẽ bắt bất cứ thứ gì bắt đầu bằng [, có bất kỳ số thập phân và dấu chấm phẩy nào, và kết thúc bằng một chữ cái. Điều này sẽ nắm bắt bất kỳ chuỗi thoát ANSI phổ biến nào .

Đối với các cuộc vui, đây là một giải pháp lớn hơn và tổng quát hơn (nhưng được thử nghiệm tối thiểu) cho tất cả các chuỗi thoát ANSI có thể hiểu được :

./somescript | sed 's/\x1B[@A-Z\\\]^_]\|\x1B\[[0-9:;<=>?]*[-!"#$%&'"'"'()*+,.\/]*[][\\@A-Z^_`a-z{|}~]//g'

(và nếu bạn gặp sự cố SI của @ edi9999, hãy thêm | sed "s/\x0f//g"vào cuối; cách này hoạt động cho mọi char điều khiển bằng cách thay thế 0fbằng hex của char không mong muốn)


Công cụ này hoạt động độc đáo để xâu chuỗi màu ra khỏi đầu ra được bổ sung Azure az cli.
volvox

Đã sửa lỗi @elig. Hóa ra nó có một số vấn đề, bắt đầu với một số trình soạn thảo thay thế tất cả các dấu gạch ngang của tôi bằng các phiên bản unicode kỳ lạ, nhưng cũng có một loạt thoát không đúng cách - |trong sed, ]bên trong một lớp ký tự trong sed và 'trong một chuỗi bash trích dẫn. Bây giờ nó đang làm việc cho tôi cho một trường hợp thử nghiệm rất cơ bản.
meustrus

20

Đối với Mac OSX hoặc BSD, hãy sử dụng

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'

1
Thật kỳ lạ, cái này hoạt động tốt cho debian nhưng cái khác ở trên thì không.
cy8g3n

Điều này một phần làm việc. Tuy nhiên, nếu tôi mở một tệp trong excel, tôi vẫn thấy ký tự đặc biệt này "?" ở cuối mỗi dòng.
doudy_05

@ doudy_05 Hãy thử chuyển -Ecờ cho sed để bật regrec mở rộng.
Alexander Zinchenko

14

Tôi cũng có một vấn đề là đôi khi, nhân vật SI xuất hiện.

Nó đã xảy ra ví dụ với đầu vào này: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Đây là một cách để loại bỏ ký tự SI (shift in) (0x0f)

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sed "s/\x0f//g"

2
Không chắc chắn tại sao câu trả lời này nhận được rất ít tín dụng. Đây là người duy nhất làm việc cho tôi ...
m8mble

8

Hmm, không chắc chắn điều này có hiệu quả với bạn không, nhưng 'tr' sẽ 'dải' (xóa) mã kiểm soát - thử:

./somescript | tr -d '[:cntrl:]'

32
Đột nhiên, nó cũng xóa các dòng mới
ruX

Có, LF và CR (mã) là mã kiểm soát; nếu bạn quan tâm đến nhiều hơn một dòng thì đây có thể không phải là một giải pháp. Vì có vẻ như bạn đang chạy chương trình JAVA, tôi sẽ đoán rằng màu sắc được quản lý từ đó; Nếu không, bạn sẽ cần xem thiết lập bảng điều khiển của mình (ví dụ: cài đặt đầu cuối / lược đồ màu) và / hoặc tại các tùy chọn cho mỗi lệnh hỗ trợ 'màu sắc', tức là ls --color = never
Dale_Reagan

3
Tôi thích câu trả lời này vì sự thanh lịch của nó, ngay cả khi nó không chỉ là loại bỏ màu sắc. Cảm ơn!
Johann Philipp Strathausen

7
nó thực sự để mã ở đó, xem ls -l + lệnh của bạn:rwxr-xr-x 1 tokra admin 22 Oct 18 14:21 [0m[01;36m/usr/local/opt/gradle[0m -> [01;34m../Cellar/gradle/4.2.1[0m/
Tới Kra

7

Tôi đã có một vấn đề tương tự. Tất cả các giải pháp tôi tìm thấy đã hoạt động tốt cho các mã màu nhưng không xóa các ký tự được thêm bởi "$(tput sgr0)"(đặt lại các thuộc tính).

Lấy ví dụ, giải pháp trong nhận xét của davemyron độ dài của chuỗi kết quả trong ví dụ dưới đây là 9, không phải 6:

#!/usr/bin/env bash

string="$(tput setaf 9)foobar$(tput sgr0)"
string_sed="$( sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" <<< "${string}" )"
echo ${#string_sed}

Để hoạt động chính xác, regex phải được mở rộng để khớp với chuỗi được thêm bởi sgr0(" \E(B"):

string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"

@Jarodiv - cảm ơn vì cách tiếp cận toàn diện nhất. Tất cả các câu trả lời được cung cấp trong chủ đề này chỉ xử lý các chuỗi Điều khiển ANSI / VT100 (ví dụ: "\ e [31mHello World \ e [0m"), tuy nhiên không khắc phục bất cứ điều gì gây ra bởi định dạng văn bản TPUT (ví dụ: tput smso / tput setaf X / tput rmso / tput sgr0). Kết quả là sau tất cả các vụ hành quyết 'sed', vẫn còn một số mớ hỗn độn khác trong nhật ký. Đây là một giải pháp thuần túy cho các giai đoạn của tôi!
vô danh

5

Hàm đơn giản hơn nhiều trong Bash thuần để lọc các mã ANSI phổ biến từ luồng văn bản:

# Strips common ANSI codes from a text stream

shopt -s extglob # Enable Bash Extended Globbing expressions
ansi_filter() {
  local line
  local IFS=
  while read -r line || [[ "$line" ]]; do
    echo "${line//$'\e'[\[(]*([0-9;])[@-n]/}"
  done
}

Xem:

  1. linuxjournal.com: Globbing mở rộng
  2. gnu.org: Mở rộng tham số Bash

1
Điều này không hoạt động. Kiểm tra với tldr. (Mặc dù tôi sử dụng zsh nên cũng có thể là vì điều đó.)
HappyFace

Thật vậy, Zsh sẽ không hiểu sự hả hê kéo dài của Bash extglobhoặc có lẽ nó cũng sẽ không hiểu thay thế chuỗi hoàn toàn.
Léa Gris

Tôi đã kích hoạt phần mở rộng của zsh ... Thay thế chuỗi cũng phải là posix?
HappyFace

Thay thế chuỗi không phải là POSIX. Bạn có thể sử dụng bất kỳ phương pháp thay thế nào bằng cách sử dụng sedđược đề cập ở đây sẽ hoạt động với Zsh.
Léa Gris

Giải pháp này có lợi thế là đệm dòng văn bản. Tôi đã thử với sed nhưng nó đã chặn đường ống của tôi.
Guillermo Prandi

3

Giải pháp của @ jeff-Bowman đã giúp tôi thoát khỏi MỘT SỐ mã màu. Tôi đã thêm một phần nhỏ vào regex để xóa thêm một số:

sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" # Original. Removed Red ([31;40m[1m[error][0m)
sed -r "s/\x1B\[([0-9];)?([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" # With an addition, removed yellow and green ([1;33;40m[1m[warning][0m and [1;32;40m[1m[ok][0m)
                ^^^^^^^^^
                remove Yellow and Green (and maybe more colors)

2

Đây là một giải pháp Bash thuần túy.

Lưu dưới dạng strip-escape-codes.sh, thực hiện và sau đó chạy <command-producing-colorful-output> | ./strip-escape-codes.sh.

Lưu ý rằng điều này loại bỏ tất cả các mã / trình tự thoát ANSI. Nếu bạn muốn dải màu chỉ, thay thế [a-zA-Z]bằng "m".

Bash> = 4.0:

#!/usr/bin/env bash

# Strip ANSI escape codes/sequences [$1: input string, $2: target variable]
function strip_escape_codes() {
    local _input="$1" _i _char _escape=0
    local -n _output="$2"; _output=""
    for (( _i=0; _i < ${#_input}; _i++ )); do
        _char="${_input:_i:1}"
        if (( ${_escape} == 1 )); then
            if [[ "${_char}" == [a-zA-Z] ]]; then
                _escape=0
            fi
            continue
        fi
        if [[ "${_char}" == $'\e' ]]; then
            _escape=1
            continue
        fi
        _output+="${_char}"
    done
}

while read -r line; do
    strip_escape_codes "${line}" line_stripped
    echo "${line_stripped}"
done

Bash <4.0:

#!/usr/bin/env bash

# Strip ANSI escape codes/sequences [$1: input string, $2: target variable]
function strip_escape_codes() {
    local input="${1//\"/\\\"}" output="" i char escape=0
    for (( i=0; i < ${#input}; ++i )); do         # process all characters of input string
        char="${input:i:1}"                       # get current character from input string
        if (( ${escape} == 1 )); then             # if we're currently within an escape sequence, check if
            if [[ "${char}" == [a-zA-Z] ]]; then  # end is reached, i.e. if current character is a letter
                escape=0                          # end reached, we're no longer within an escape sequence
            fi
            continue                              # skip current character, i.e. do not add to ouput
        fi
        if [[ "${char}" == $'\e' ]]; then         # if current character is '\e', we've reached the start
            escape=1                              # of an escape sequence -> set flag
            continue                              # skip current character, i.e. do not add to ouput
        fi
        output+="${char}"                         # add current character to output
    done
    eval "$2=\"${output}\""                       # assign output to target variable
}

while read -r line; do
    strip_escape_codes "${line}" line_stripped
    echo "${line_stripped}"
done

Vâng, giải pháp này có thể thậm chí ít phức tạp hơn.
Alexander Zinchenko

1

Ý tưởng gây tranh cãi sẽ là cấu hình lại các thiết lập thiết bị đầu cuối cho môi trường quy trình này để cho quy trình biết rằng thiết bị đầu cuối không hỗ trợ màu sắc.

Một cái gì đó như TERM=xterm-mono ./somescriptđến với tâm trí của tôi. YMMV với hệ điều hành cụ thể của bạn và khả năng tập lệnh của bạn để hiểu các cài đặt màu của thiết bị đầu cuối.


-7

Điều này làm việc cho tôi:

./somescript | cat

3
Điều đó phụ thuộc vào cách thức somescriptthực hiện. Nó có thể hoặc không thể nhận ra rằng đầu ra tiêu chuẩn của nó là một tty. (Các từ phạm tội thực sự mã hóa mã thoát cụ thể của thiết bị đầu cuối vào chương trình và phá vỡ khủng khiếp khi được sử dụng trên các thiết bị đầu cuối khác hoặc trong các tập lệnh).
Toby Speight

Cảm ơn Toby. Tôi đã sử dụng quản lý của django để kiểm tra, nhưng những gì bạn nói có ý nghĩa.
nhện nhện
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.