Linux

使用 tee 從 GitHub 複製管道輸出未命中輸出

  • January 11, 2022

我想我會將複製 GitHub 儲存庫的輸出記錄到文件中。如果我跑

git clone git@github.com:my_repo/my_repo.git | tee -a log

我得到一個空log文件,但命令行輸出如下:

Cloning into 'my_repo'...
remote: Enumerating objects: 284, done.
remote: Counting objects: 100% (228/228), done.
remote: Compressing objects: 100% (133/133), done.
remote: Total 284 (delta 122), reused 162 (delta 69), pack-reused 56
Receiving objects: 100% (284/284), 873.22 KiB | 2.71 MiB/s, done.
Resolving deltas: 100% (138/138), done.

這對我來說已經有點神秘了。我想我應該嘗試通過管道傳輸stderrstdout傳輸到文件

git clone git@github.com:my_repo/my_repo.git 2>&1 | tee -a log

現在log文件和命令行都得到了

複製到“my_repo”…

我在這裡想念什麼?如何將這些remote: ...行放入文件中tee

如評論中所述,git檢查標準輸出/錯誤是否是終端。如果是,程序會在終端輸出中添加一些額外的糖,以文本形式指示進度和一些統計資訊。如果不是,它會將標準流的輸出保持在最低限度。

在內部,它使用isatty() 例如此處所示的範例。這是命令行程序的一種常見做法。最重要的是要剝離 ANSI 著色,因為您通常不希望將其放入文件中,弄亂管道處理等。例如檢查:

/bin/ls --color=auto -1 # ls print column in pipe
/bin/ls --color=auto | sed -n 'l'
/bin/ls --color=always | sed -n 'l'

可以選擇通過git-q--quiet始終抑製或--progress始終列印來覆蓋這一點。進度輸出被列印到stderr而不是(通常)用於消費。儘管如此:

git clone --progress foo/bar.git 2>&1 | tee log

其他程序可能沒有類似的選項,並且必須跳過一些障礙才能獲取數據。這樣做是為了盡量減少混亂等,例如在一個仍然列印到的自動腳本中stderr,但將其減少為錯誤消息。例如git

$ git clone -q https://nonexistig-url.com/foo.git
fatal: https://nonexistig-url.com/foo.git/info/refs not valid: 
      is this a git repository?
$ echo $?
128

沒有進展,但人們會收到錯誤消息,這通常在事後場景中更有趣。複製過程本身的進展是沉默的。

有時可以通過後處理或執行其他或附加命令通過其他方式獲取資訊。例如git count-objects -v.


假終端

如果一個人真的想要被抑制的輸出,並且程序沒有類似的選項,那麼git一種選擇是讓程序“認為”它寫入終端,即使它沒有。實現這一目標的各種方法;我用於臨時事物的一種方法是我只是出於某種原因想要它,即用始終返回 true的系統替換系統isatty()

echo 'int isatty(int fd) { return 1; }' | \
gcc -O2 -fpic -shared -ldl -o faketty.so -xc -

strip faketty.so
chmod 400 faketty.so

與包裝腳本一起使用faketty

#! /bin/sh -
LD_PRELOAD=/path/to/faketty.so "$@"

並用作:

faketty git clone foo

正如程式碼所示,它確實不做任何文件描述符檢查等,而只是一個 hack。


出生數據的問題

git clone命令不會為輸出著色,但會顯示進度。這樣做的方法是重複覆蓋終端中的同一行。它通常寫道:

Cloning into 'foo'...\n
remote: Enumerating objects: 236, done.        \n
\rReceiving objects:   0% (1/236)   
\rReceiving objects:   1% (3/236)   
\rReceiving objects:   2% (5/236)   
...
\rReceiving objects:  79% (187/236)   
\rremote: Total 236 (delta 0), reused 0 (delta 0), pack-reused 236        \n
...
\rReceiving objects:  99% (234/236)   
\rReceiving objects: 100% (236/236)   
\rReceiving objects: 100% (236/236), 79.45 MiB | 104.73 MiB/s, done.\n
...

在這裡\r,就像在輸入中一樣,告訴終端將游標移動到首,而不是\n像在換行/行尾中那樣,將游標移動到下一行的開頭。

結果,每次寫入都會覆蓋前一行,直到該remote: Total 236 …行結束,\n並且該過程在下一行繼續,從而為查看者帶來類似這樣的結果:

Cloning into 'foo'...
remote: Total 236 (delta 0), reused 0 (delta 0), pack-reused 236        
Receiving objects: 100% (236/236), 79.45 MiB | 104.73 MiB/s, done.
...

包括每次寫入的視覺進度。對於這種情況cat,當終端解釋輸入時,一個簡單的日誌看起來還可以,但 ased -n l log將顯示真實內容。(可選sed 's/\r/\n/g' log)顯示*“線條”*之類的東西。

清理

對於這種情況,一個簡單的管道或日誌槽的後處理sed就足夠了:

sed 's/.*\r//'
# Optionally to trim trailing white space, either of:
sed 's/.*\r//;s/ \+$//'
sed 's/.*\r\| \+$//g'

它有點脆弱,因為進度輸出可能會在版本、版本等之間發生變化,因此不能保證資訊會相同。如果輸出有顏色,則需要進一步刪除這些序列。

一個人將失去顯示的進度(實時計數),並且只有在命令之前通過管道傳輸時才能獲得最終git結果。sed``tee

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