Grep

tr 命令無法處理從 grep 管道傳輸的顏色輸出

  • May 23, 2022

我正在執行 macOS 12.3.1

我在我的 .zshrc 中添加了幾行,即。

export GREP_OPTIONS='--color=always'
export GREP_COLOR='1;35;40'

在此之後,當我將 grep 輸出通過管道傳輸到 tr 時,它返回相同數量的行,但所有行都是空白的

例如:

grep ^.....$ /usr/share/dict/words | tr "[:lower:]" "[:upper:]"

返回 10230 個空行。

這是預期的嗎?

要以顏色輸出匹配,grep請在匹配前後寫入著色轉義序列。

這些是對終端更改背景和/或前景色的指令。

重要的是要意識到它與文本一起出現在輸出中。您看不到它,因為您的終端不會將它們顯示為圖形符號,而是將其理解為特殊說明。

轉義序列以 ESC 字元(ASCII aka \eor中的 0x1b 字節(八進制中的 033) ^[)開頭,然後是一些本身不必是控製字元的字元。

您可以通過將輸出傳遞到以下內容來顯示這些字元:

$ echo + | grep --color=always . | sed -n l
\033[01;31m\033[K+\033[m\033[K$
$ echo + | grep --color=always . | od -An -vtc -tx1 -to1
033   [   0   1   ;   3   1   m 033   [   K   + 033   [   m 033
 1b  5b  30  31  3b  33  31  6d  1b  5b  4b  2b  1b  5b  6d  1b
033 133 060 061 073 063 061 155 033 133 113 053 033 133 155 033
  [   K  \n
 5b  4b  0a
133 113 012

(這裡還包括各個字節的十六進制和八進制值)

或者(儘管非標準且模棱兩可):

$ echo + | grep --color=always . | cat -A
^[[01;31m^[[K+^[[m^[[K$

grep您可以在輸出中看到,在\e[01;31\e[K比賽之前和比賽\e[m\e[K之後都有一個。

給定終端辨識哪些轉義序列以及如何隨終端而變化。例如,對於 xterm,請參閱那裡的規範。這些天以上的人相當普遍。

對於以 開頭\e[和結尾的m那個,終端將每個 -;分隔的數字理解為不同的渲染屬性,以應用於從現在開始編寫的文本。例如1粗體,31將前景色設置為紅色。

\e[K是告訴終端從游標到行尾清除螢幕的轉義序列。

所以終端實際上看到:

<bold-fg_red><clear-to-eol>+<reset-all-attributes><clear-to-eol>

但所有tr看到的是那些 ESC, [, …m以及它被要求音譯的其他那些。

特別是這裡,它會音譯mM,而改變顏色屬性的轉義序列會一起變成​​別的東西。

要了解轉義序列及其作用,而不是查看終端文件(例如上面提到的 xterm 的https://www.invisible-island.net/xterm/ctlseqs/ctlseqs.html),這有時很難要查找或不存在,您還可以查看數據庫,該terminfo數據庫記錄了一些終端辨識的轉義序列,用於一些常見的操作。

您可以使用以下命令為您的終端(由$TERM環境變數標識)手動查詢該數據庫infocmp

$ infocmp -xL1 | grep M,
       delete_line=\E[M,
       key_enter=\EOM,
       key_mouse=\E[M,
       parm_delete_line=\E[%p1%dM,
       scroll_reverse=\EM,

對於這些操作(功能)的詳細資訊,您可以查看terminfo(5)手冊頁(man 5 terminfo)。

\e[M( delete_line) 刪除一行,\e[<decimal>M( parm_delete_line) 刪除<decimal>行。因此,一旦音譯為大寫,您的著色序列就變成了行刪除序列。

您通常不想對彩色輸出進行後處理,因為它們僅適用於終端。這就是為什麼大多數支持著色的命令在其輸出不進入終端時會禁用它。

對於 GNU grep,正如您已經發現的那樣,您需要--color=auto(或grep --color)獲得該行為。

現在,如果您仍想查看顏色,則需要將顏色移至管道中的最後一個命令,即輸出到終端的命令:

<file tr '[:lower:]' '[:upper:]' | grep  -xE --colour=auto '.{5}'

在這裡使用--colour=auto,如果包含該命令的腳本將其輸出重定向/後處理,則禁用著色。

在這裡,由於正則表達式匹配整行(-x上面的選項,這避免了在您的方法中使用^$喜歡),您不妨將前景色切換為紅色,然後清除屬性:

if [ -t 1 ]; then
 tput setaf 1 # set ANSI foreground colour
 tput bold
fi
grep...
if [ -t 1 ]; then
 tput sgr0 # turn off all attributes
fi

這裡用於為您的終端tput查詢terminfo正確的序列,但由於大多數終端都這樣做,所以喜歡grep並硬編碼這些序列:

[ -t 1 ] && printf '\33[1;31m'
grep...
[ -t 1 ] && printf '\e[m'

用於[ -t 1 ]檢查標準輸出(文件描述符 1)是否為終端。

引用自:https://unix.stackexchange.com/questions/703422