零/空分隔符打破列命令
問題
我想解析一些結構為行(
\n
分隔)的數據,欄位由NUL
字元分隔\0
。許多 linux 命令使用諸如
--zero
forfind
或-0
for之類的選項來處理此分隔符,或者xargs
將分隔符定義為\0
forgawk
。我沒能理解如何將
column
解釋NUL
作為分隔符。例子
如果您生成以下數據集(2 行 3 列,用 分隔
\0
):echo -e "line1\nline2" | awk 'BEGIN {OFS="\0"} {print $1"columnA",$1"columnB",$1"columnC"}'
您將獲得預期的以下輸出(
\0
不會顯示分隔符,而是分隔每個欄位):line1columnAline1columnBline1columnC line2columnAline2columnBline2columnC
但是,當我嘗試使用 column 來顯示我的列時,儘管通過
\0
了 ,但由於某種原因,輸出僅顯示第一列:echo -e "line1\nline2" \ | awk 'BEGIN {FS="\0"; OFS="\0"} {print $1"columnA",$1"columnB",$1"columnC"}' | column -s '\0'
line1columnA line2columnA
實際上,即使不提供分隔符,列似乎也會在 nul 字元上中斷:
echo -e "line1\nline2" \ | awk 'BEGIN {FS="\0"; OFS="\0"} {print $1"columnA",$1"columnB",$1"columnC"}' | column
line1columnA line2columnA
問題
- 有沒有辦法在中
\0
用作欄位/列分隔符column
?- 可選/獎勵問題:為什麼列的行為是這樣的(
\0
如果不進行管理,我希望將完全忽略,並且將整行列印為單個欄位)?- 可選/獎勵問題 2:這些列中的一些數據將是文件路徑,我想將其
\0
用作最佳實踐。您是否有更好的做法來建議在文件中儲存“隨機字元串”而不必轉義它們可能包含的潛在衝突欄位分隔符?
有沒有辦法在 column 中使用 \0 作為欄位/列分隔符?
不,因為
column
(我知道)的兩種實現,即歷史上的 BSD和util-linux 包中的一種,都使用標準 C 庫的字元串操作函式來解析輸入行,並且這些函式在假設下工作該字元串是 NUL 終止的。換句話說,一個 NUL 字節意味著總是標記任何字元串的結尾。可選/獎勵問題:為什麼列的行為是這樣的(如果不進行管理,我希望 \0 將被完全忽略,並且整行將作為單個欄位列印)?
除了我上面解釋的內容之外,請注意該選項
-s
需要文字字元。它不會解析類似的轉義語法\0
(也不會\n
如此)。這意味著您告訴column
將 a\
和 a0
作為其輸入的有效分隔符。
$''
如果您使用支持它的眾多 shell 之一(例如,它在 中可用bash
但在 中不可用),則可以通過字元串語法提供轉義序列dash
。因此,例如column -s $'\n'
,如果由這些 shell 之一執行,它將是有效的(指定 <newline> 作為列分隔符)。作為旁注,我不清楚您對
column
. 即使它確實支持 NUL 作為分隔符,它也會將該輸入的每一行轉換為輸出的一整列。也許您還想使用-t
以便為每行的單個欄位分列?可選/獎勵問題 2:這些列中的一些數據將是文件路徑,我想使用 \0 作為最佳實踐。您是否有更好的做法來建議在文件中儲存“隨機字元串”而不必轉義它們可能包含的潛在衝突欄位分隔符?
我所知道的唯一一種方法是在每個欄位前面加上它的長度,以你認為合適的文本或二進製表示。但是,您肯定無法將它們通過管道傳輸到
column
.此外,如果您關心的是文件路徑,那麼您應該考慮不要將
\n
其用作“結構”分隔符,因為這對於文件名來說是一個完全有效的字元。就像概念驗證一樣,基於您的範例,但使用 NUL 作為結構/記錄分隔符和指定長度的欄位:(我還對您的範例字元串進行了一些處理以涉及多字節字元)
echo -e 'line1\nline2 ò' \ | LC_ALL=C awk ' BEGIN { ORS="\0" # here we just move arguments away from ARGV # so that awk reads input from stdin for (i in ARGV) { c[i]=ARGV[i] delete ARGV[i] } } { # first field is the line read printf "%4.4d%s", length, $0 # then a field for each argument for(i=1; i<length(c); i++) printf "%4.4d%s", length(c[i]), c[i] printf "%s", ORS } ' "€ column A" $'colu\nmnB' "column C"
使用參數來
awk
傳遞任意數量的任意列字元串。然後,一個假設的對應腳本
awk
(實際上必須是gawk
或mawk
要處理RS="\0"
):LC_ALL=C awk ' BEGIN { RS="\0" } { nf=0; while(length) { field_length = substr($0, 1, 4) printf "field %d: \"%s\""ORS, ++nf, substr($0, 5, field_length) $0 = substr($0, 5+field_length) } printf "%s", ORS } '
請注意,為兩個腳本指定相同的語言環境以匹配字元大小非常重要。指定
LC_ALL=C
兩者都很好。