การลบสีออก


140

ฉันมีสคริปต์ที่สร้างผลลัพธ์ด้วยสีและฉันต้องการลบรหัส ANSI

#!/bin/bash

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

./somescript

ผลลัพธ์คือ (ในล็อกไฟล์):

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

ฉันไม่รู้วิธีใส่อักขระ ESC ที่นี่ดังนั้นฉันจึง@เข้าแทนที่

ฉันเปลี่ยนสคริปต์เป็น:

#!/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"

แต่ตอนนี้มันให้ฉัน (ในล็อกไฟล์):

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

ฉันจะลบสิ่งนี้ได้@[60Gอย่างไร'

อาจมีวิธีปิดการใช้งานการระบายสีอย่างสมบูรณ์สำหรับสคริปต์ทั้งหมดหรือไม่


สำหรับโหนด / NPM คุณสามารถใช้strip-ansi: github.com/chalk/strip-ansi
Joshua Pinter

คำตอบ:


165

ตามที่วิกิพีเดียที่[m|K]อยู่ในsedคำสั่งที่คุณใช้ถูกออกแบบมาเพื่อการจัดการm(คำสั่งสี) และK(ที่ "ลบส่วนหนึ่งของสายคำสั่ง") สคริปต์ของคุณพยายามกำหนดตำแหน่งเคอร์เซอร์สัมบูรณ์เป็น 60 ( ^[[60G) เพื่อให้ได้ OK ทั้งหมดในบรรทัดซึ่งsedบรรทัดของคุณไม่ครอบคลุม

(อย่างถูกต้อง[m|K]น่าจะเป็น(m|K)หรือ[mK]เพราะคุณไม่ได้พยายามจับคู่ไพพ์ แต่ตอนนี้มันไม่สำคัญเลย)

หากคุณสลับการจับคู่สุดท้ายในคำสั่งของคุณเป็น[mGK]หรือ(m|G|K)คุณควรจะสามารถจับลำดับการควบคุมพิเศษนั้นได้

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

29
ผู้ใช้ BSD / OSX: เรามักจะไม่มีตัวเลือก -r ในการระงับ brew install gnu-sedจะติดตั้งรุ่นที่มีความสามารถ gsedทำงานด้วย
Nicolai S

1
ถ้าเป็นเช่นecho "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -Aนั้นฉันจะได้รับ: foo^O bar$ดังนั้นฉันเดาว่าตัวละครบางตัวจะไม่ถูกลบอย่างถูกต้องใช่มั้ย คุณรู้วิธีการแก้ไขหรือไม่?
edi9999

1
@ edi9999 เท่าที่ฉันจะบอกได้ความแตกต่างนั่นคือการตั้งค่าสีที่เกิน 16 สี (เท่าที่setafรองรับ) ต้องใช้พารามิเตอร์มากกว่าสอง regex ของฉันรองรับสอง เปลี่ยนคนแรกที่?ออกมาเพื่อ*จะช่วยให้ การจัดการsgr0เป็นไปได้ แต่ขึ้นอยู่กับการค้นหามันมีแนวโน้มที่จะเติบโตนอกขอบเขตของคำตอบที่อิงกับแฮ็ค
Jeff Bowman

ตกลงฉันได้เพิ่มคำตอบที่เพิ่มsedไปยังไปป์เพื่อตัดอักขระ "shift in"
edi9999

7
สิ่งนี้ไม่ทำงานอย่างน่าเชื่อถือเนื่องจากอาจมีค่าที่สาม (ala [38;5;45m) คำตอบอื่นนี้ใช้งานได้unix.stackexchange.com/a/55547/168277
davemyron

30

ฉันไม่สามารถรับผลลัพธ์ที่ดีจากคำตอบอื่น ๆ ได้ แต่สิ่งต่อไปนี้ใช้ได้สำหรับฉัน:

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

ถ้าฉันเพียงลบตัวควบคุมถ่าน "^ [" มันจะเหลือข้อมูลสีที่เหลือเช่น "33m" การใส่รหัสสีและ "m" นั้นเป็นการหลอกลวง ฉันงงกับ s / \ x1B // g ไม่ทำงานเพราะ \ x1B [31m ใช้ได้กับเสียงก้องอย่างแน่นอน


6
บน OSX (BSD sed) ให้ใช้-Eแทน-rregex แบบขยาย สามารถพบได้มากขึ้นที่นี่
Assambar

ฉันต้องเปลี่ยน{1,3}ไปเป็น{,3}(มิฉะนั้นมันก็ยังข้ามการควบคุมบางอย่าง) ขอบคุณสำหรับการแก้ปัญหาของคุณ!
กระทำที่ไม่เหมาะสม

6
เนื่องจากอาจมีหลายตัวเลขคั่นด้วยเซมิโคลอน (สำหรับสีพื้นหลังตัวหนาตัวเอียง ฯลฯ ... ) คำสั่งนี้ใช้งานได้สำหรับฉัน:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu

อันนี้ (จากหลาย ๆ อันที่ฉันทดสอบ) ทำงานกับเอาต์พุต Ansible ที่รันด้วย unbuffer
Martin

23

IMHO คำตอบส่วนใหญ่พยายามอย่างหนักเกินไปที่จะ จำกัด สิ่งที่อยู่ในรหัสหลบหนี เป็นผลให้พวกเขาสิ้นสุดรหัสทั่วไปที่ขาดหายไปเช่น[38;5;60m(เบื้องหน้า ANSI สี 60 จากโหมด 256 สี)

พวกเขายังจำเป็นต้องมี-rตัวเลือกที่ช่วยให้ส่วนขยาย GNU สิ่งเหล่านี้ไม่จำเป็น พวกเขาแค่ทำให้การอ่าน regex ดีขึ้น

นี่คือคำตอบที่ง่ายขึ้นที่จัดการกับการหลบหนี 256 สีและทำงานบนระบบที่ไม่ใช่ GNU sed:

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

สิ่งนี้จะจับทุกสิ่งที่ขึ้นต้นด้วย[มีจำนวนทศนิยมและอัฒภาคจำนวนเท่าใดก็ได้และลงท้ายด้วยตัวอักษร สิ่งนี้ควรจับลำดับการหลีกหนีใด ๆ ของANSI ทั่วไปทั่วไปลำดับหนี

สำหรับ funsies ต่อไปนี้เป็นโซลูชันที่มีขนาดใหญ่ขึ้นและทั่วไปมากขึ้น (แต่ทดสอบน้อยที่สุด) สำหรับลำดับการหลบหนี ANSI ที่เป็นไปได้ทั้งหมด :

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

(และหากคุณมีปัญหา SI ของ @ edi9999 ให้เพิ่ม| sed "s/\x0f//g"ไปยังส่วนท้ายซึ่งใช้งานได้อักขระควบคุมใด ๆโดยแทนที่0fด้วย hex ของอักขระที่ไม่ต้องการ)


อันนี้ใช้ได้ดีกับสีของสายออกจาก Azure az cli prettified เอาท์พุท
volvox

แก้ไข @elig ปรากฎว่ามันมีปัญหาหลายอย่างเริ่มจากตัวแก้ไขบางตัวแทนที่เครื่องหมายขีดคั่นของฉันทั้งหมดด้วยรุ่นยูนิโค้ดแปลก ๆ แต่ก็มีกลุ่มของการหลบหนีที่ไม่เหมาะสม - |ใน sed, ]ภายในคลาสของตัวละครใน sed และ'ในสตริงทุบตี ตอนนี้มันทำงานสำหรับฉันสำหรับกรณีทดสอบพื้นฐานมาก
meustrus

20

สำหรับ Mac OSX หรือ BSD ใช้

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

1
แปลกสิ่งนี้ใช้ได้ดีสำหรับเดเบียน แต่อย่างอื่นไม่ได้
cy8g3n

อันนี้ทำงานได้เพียงบางส่วน อย่างไรก็ตามถ้าฉันเปิดไฟล์ใน excel ฉันยังคงเห็นอักขระพิเศษนี้ "?" ในตอนท้ายของแต่ละบรรทัด
doudy_05

@ doudy_05 พยายามที่จะส่งผ่าน-Eธงสำหรับ sed เพื่อเปิดใช้งาน regexp แบบขยาย
Alexander Zinchenko

14

ฉันยังมีปัญหาที่บางครั้งตัวละคร SI ก็ปรากฏตัวขึ้น

มันเกิดขึ้นเช่นกับอินพุตนี้: echo "$(tput setaf 1)foo$(tput sgr0) bar"

ต่อไปนี้เป็นวิธีในการตัดอักขระ SI (เลื่อนลง) (0x0f)

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

2
ไม่แน่ใจว่าทำไมคำตอบนี้ได้รับเครดิตน้อยมาก นี่เป็นคนเดียวที่ทำงานให้ฉัน ...
m8mble

8

อืมไม่แน่ใจว่าสิ่งนี้จะใช้ได้กับคุณหรือไม่ แต่รหัสควบคุม 'tr' จะ 'ตัด' (ลบ) - ลอง:

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

32
ทันใดนั้นมันก็ลบบรรทัดใหม่
ruX

ใช่ LF และ CR (รหัส) เป็นรหัสควบคุม หากคุณสนใจมากกว่าหนึ่งบรรทัดนี่อาจไม่ใช่วิธีแก้ปัญหา เนื่องจากปรากฏว่าคุณกำลังเรียกใช้โปรแกรม JAVA ฉันจะเดาว่ามีการจัดการสีจากที่นั่น มิฉะนั้นคุณจะต้องดูการตั้งค่าคอนโซลของคุณ (เช่นการตั้งค่าเทอร์มินัล / ชุดรูปแบบสี) และ / หรือที่ตัวเลือกสำหรับแต่ละคำสั่งที่รองรับ 'colors' เช่น ls --color = never
Dale_Reagan

3
ฉันชอบคำตอบนี้เพราะความสง่างามแม้ว่ามันจะทำได้มากกว่าแค่การลบสี ขอบคุณ!
โยฮันน์ฟิลิปป์สตราทาเซ่น

7
มันให้รหัสที่นั่นดู ls -l + คำสั่งของคุณ: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/
ถึง Kra

7

ฉันมีปัญหาที่คล้ายกัน โซลูชันทั้งหมดที่ฉันพบทำงานได้ดีสำหรับรหัสสี แต่ไม่ได้ลบอักขระที่เพิ่มโดย"$(tput sgr0)"(รีเซ็ตแอตทริบิวต์)

ยกตัวอย่างเช่นการแก้ปัญหาในความคิดเห็นโดย davemyronความยาวของสตริงผลลัพธ์ในตัวอย่างด้านล่างคือ 9 ไม่ใช่ 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}

เพื่อให้ทำงานได้อย่างถูกต้อง regex ต้องถูกขยายให้ตรงกับลำดับที่เพิ่มโดยsgr0(" \E(B"):

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

@Jarodiv - ขอบคุณสำหรับวิธีการที่ครอบคลุมที่สุด คำตอบทั้งหมดที่มีให้ในหัวข้อนี้จัดการเฉพาะกับลำดับการควบคุม ANSI / VT100 (เช่น: "\ e [31mHello World \ e [0m") อย่างไรก็ตามอย่าแก้ไขสิ่งที่เกิดจากการจัดรูปแบบข้อความของ TPUT (เช่น tput smso / tput setaf X / tput rmso / tput sgr0) ผลที่ตามมาหลังจากการประหารชีวิตแบบ 'sed' ทั้งหมดมีความยุ่งเหยิงอื่น ๆ ที่เหลืออยู่ในบันทึก นี่เป็นทางออกที่บริสุทธิ์สำหรับผู้ใช้ของฉัน!
faceless

5

ฟังก์ชั่นที่ง่ายกว่ามากใน Bash บริสุทธิ์เพื่อกรองรหัส ANSI ทั่วไปจากสตรีมข้อความ:

# 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
}

ดู:

  1. linuxjournal.com: ขยาย Globbing
  2. gnu.org: การขยายพารามิเตอร์ Bash

1
มันใช้งานไม่ได้ tldrการทดสอบด้วย (แม้ว่าฉันจะใช้ zsh ดังนั้นอาจเป็นเพราะเรื่องนั้น)
HappyFace

ที่จริงแล้ว Zsh จะไม่เข้าใจการขยายตัวของ Bash extglobหรืออาจจะไม่เข้าใจการแทนที่สตริงทั้งหมด
Léa Gris

ฉันเปิดใช้งานส่วนขยายของ zsh ... การแทนที่สตริงควรเป็น posix ด้วยหรือไม่
HappyFace

การแทนที่สตริงไม่ใช่ POSIX คุณสามารถใช้วิธีอื่นใดก็ได้โดยใช้วิธีที่sedกล่าวถึงที่นี่ซึ่งจะทำงานกับ Zsh
Léa Gris

วิธีนี้มีข้อดีของการบัฟเฟอร์ข้อความ ฉันพยายามด้วย sed แต่มันเป็นบล็อกบัฟเฟอร์ท่อของฉัน
Guillermo Prandi

3

วิธีการแก้ปัญหาของ @ jeff-bowman ช่วยให้ฉันกำจัดรหัสสีบางส่วนได้ ฉันเพิ่มอีกส่วนเล็ก ๆ ใน regex เพื่อลบเพิ่มเติม:

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

นี่เป็นวิธีการทุบตีบริสุทธิ์

บันทึกเป็นstrip-escape-codes.shทำให้ปฏิบัติการแล้วเรียกใช้<command-producing-colorful-output> | ./strip-escape-codes.shให้ปฏิบัติการและดำเนินการแล้ว

โปรดทราบว่าสิ่งนี้จะปลดเปลื้องรหัส / ลำดับ ANSI ทั้งหมด หากคุณต้องการตัดสีเท่านั้นให้แทนที่[a-zA-Z]ด้วย"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

วิธีนี้อาจซับซ้อนน้อยกว่ากัน
Alexander Zinchenko

1

แนวคิดที่แย้งคือการกำหนดการตั้งค่าเทอร์มินัลใหม่สำหรับสภาพแวดล้อมกระบวนการนี้เพื่อให้กระบวนการทราบว่าเทอร์มินัลไม่รองรับสี

บางสิ่งบางอย่างTERM=xterm-mono ./somescriptในใจฉัน YMMV พร้อมระบบปฏิบัติการเฉพาะและความสามารถของสคริปต์ในการทำความเข้าใจการตั้งค่าสีเทอร์มินัล


-7

สิ่งนี้ใช้ได้กับฉัน:

./somescript | cat

3
ขึ้นอยู่กับวิธีsomescriptการใช้งาน มันอาจจะหรืออาจจะไม่รับรู้ว่าเอาท์พุทมาตรฐานของมันคือ tty (คำว่าผู้กระทำความผิดจริงรหัสการหลบหนีเฉพาะรหัสเทอร์มินัลเฉพาะลงในโปรแกรมและแตกอย่างน่ากลัวเมื่อใช้กับเทอร์มินัลอื่นหรือในสคริปต์)
Toby Speight

ขอบคุณโทบี ฉันใช้ Manage.py ของ django เพื่อทดสอบ แต่สิ่งที่คุณพูดมีเหตุผล
spiderlama
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.