[英]Extract columns from tab separated file
我有一個格式如下的文件( data.rdb
):
col1 col2 col3 col4 col5 col6 col7
aaa1 bbb1 ccc1 ddd1 eee1 fff1 ggg1
aaa2 bbb2 ccc2 ddd2 eee2 fff2 ggg2
aaa3 bbb3 ccc3 ddd3 eee3 fff3 ggg3
數據的一些屬性:
我需要使用 bash 按名稱提取一些列,例如col1
、 col3
和col6
,其中要選擇的列來自定義為COLUMN_LIST=$@
的 shell 變量,其中$@
是傳遞給我的 shell 腳本的參數。 每次調用腳本時,參數的數量和名稱可能會發生變化。
腳本需要在 bash 中,不能是 python 或類似的。
有任何想法嗎? 我想過使用awk
/ gawk
,但我不知道如何按列名進行選擇。 列順序可能會因文件而異。
謝謝豪爾赫
更新
出於某種原因,這些解決方案似乎都不適用於我的真實數據文件(即,我根本沒有得到任何輸出),所以我發布了其中一個的子集:
date star jdb texp
2013-11-22 epsInd 2400000.23551544 100.
2013-11-22 epsInd 2400000.23551544 100.
2013-11-22 epsInd 2400000.23551544 100.
2013-11-22 HD217987 2400000.23551544 900.
2013-11-22 TOI-134 2400000.23551544 900.
2013-11-22 tauCet 2400000.23551544 60.
2013-11-22 BD+01316 2400000.23551544 300.
2013-11-22 BD+01316 2400000.23551544 300.
2013-11-22 BD+01316 2400000.23551544 300.
2013-11-22 BD+01316 2400000.23551544 300.
在這種情況下,我會對列star
jdb
和texp
更新 2
我使用了@EdMorton 的代碼,結果如下:
date star jdb texp date star jdb texp
2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100.
2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100.
2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100.
2013-11-22 HD217987 2400000.23551544 900. 2013-11-22 HD217987 2400000.23551544 900.
2013-11-22 TOI-134 2400000.23551544 900. 2013-11-22 TOI-134 2400000.23551544 900.
2013-11-22 tauCet 2400000.23551544 60. 2013-11-22 tauCet 2400000.23551544 60.
2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300.
2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300.
2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300.
2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300.
更新 3
我最終使用了 EdMorton 的awk
版本——主要是為了輸出的靈活性——但經過修改,我不希望它輸出錯誤的列:
BEGIN {
numCols = split(column_list,cols)
OFS="\t"
}
{ sub(/\r$/,"") }
NR==1 {
for (fldNr=1; fldNr<=NF; fldNr++) {
f[$fldNr] = fldNr
}
}
{
for (colNr=1; colNr<=numCols; colNr++) {
colName = cols[colNr]
colVal = (colName in f ? $(f[colName]) : "")
printf "%s%s", colVal, (colNr<numCols ? OFS : ORS)
}
}
我遇到的主要問題是標題行沒有制表符分隔,因此列細分不起作用。 一種識別制表符/非制表符字符的簡單方法:
tr $'\t' '#' < data.rdb | head -2
這給了我的一個測試文件:
date star jdb texp
2013-11-22#epsInd#2400000.23551544#100.
列順序可能會因文件而異。
您可以使用awk
使用這種方法,它將以空格分隔的標題列名稱作為輸入,並首先通過處理第一條記錄將其轉換為列號。 一旦檢索到所需的列號,我們只需從下一行開始打印它們。
awk -v cols='col1 col3 col6' 'BEGIN {
FS=OFS="\t"
n = split(cols, a, " ")
for (i=1; i <= n; i++)
c[a[i]]
}
{
sub(/\r$/, "")
}
NR == 1 {
for (i=1; i<=NF; i++)
if ($i in c)
hdr[i]
}
{
for (i=1; i<=NF; i++)
if (i in hdr)
s = sprintf(s "%s%s", OFS, $i)
sub(OFS, "", s)
print s
s =""
} ' file | column -t
star jdb texp
epsInd 2400000.23551544 100.
epsInd 2400000.23551544 100.
epsInd 2400000.23551544 100.
HD217987 2400000.23551544 900.
TOI-134 2400000.23551544 900.
tauCet 2400000.23551544 60.
BD+01316 2400000.23551544 300.
BD+01316 2400000.23551544 300.
BD+01316 2400000.23551544 300.
BD+01316 2400000.23551544 300.
PS:添加column -t
以表格格式格式化輸出。
解決這個問題的最好方法是創建一個數組(下面的f[]
),在讀取標題行時將列標題字符串(即字段名稱)映射到字段編號,然后從那時起按字段名稱訪問字段在。
更新以防止調用者要求不存在的列名和 DOS 行結尾:
$ cat tst.awk
BEGIN {
numCols = split(column_list,cols)
FS=OFS="\t"
}
{ sub(/\r$/,"") }
NR==1 {
for (fldNr=1; fldNr<=NF; fldNr++) {
f[$fldNr] = fldNr
}
}
{
for (colNr=1; colNr<=numCols; colNr++) {
colName = cols[colNr]
colVal = (colName in f ? $(f[colName]) : (NR>1 ? "N/A" : colName))
printf "%s%s", colVal, (colNr<numCols ? OFS : ORS)
}
}
$ awk -v column_list='col1 col3 col6' -f tst.awk data.rdb
col1 col3 col6
aaa1 ccc1 fff1
aaa2 ccc2 fff2
aaa3 ccc3 fff3
$ awk -v column_list='col1 col3 col6 bob' -f tst.awk data.rdb
col1 col3 col6 bob
aaa1 ccc1 fff1 N/A
aaa2 ccc2 fff2 N/A
aaa3 ccc3 fff3 N/A
請注意,如果您願意,可以使用上述方法更改輸出列的順序,而不僅僅是按原始順序打印它們:
$ awk -v column_list='col5 col2 col4' -f tst.awk data.rdb
col5 col2 col4
eee1 bbb1 ddd1
eee2 bbb2 ddd2
eee3 bbb3 ddd3
你可以用coreutils
做到這一點。 假設您有一個包含所需列的文件 callef cols
,例如:
col2
col3
col6
您可以像這樣提取列號:
head -n1 infile | tr '\t' '\n' | grep -nf cols | cut -d: -f1 | paste -sd,
輸出:
2,3,6
將此傳遞給cut
,例如:
cut -f $(head -n1 infile | tr '\t' '\n' | grep -nf cols | cut -d: -f1 | paste -sd,) infile
輸出:
col2 col3 col6
bbb1 ccc1 fff1
bbb2 ccc2 fff2
bbb3 ccc3 fff3
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.