簡體   English   中英

使用Bash腳本選擇具有特定名稱的列和行

[英]Use Bash scripting to select columns and rows with specific name

我正在使用一個非常大的文本文件(4GB),並且我想使用僅需要的數據制作一個較小的文件。 它是制表符分隔的文件,並且具有行標題和列標題。 我基本上想選擇具有給定列和/或行名稱的數據子集。

     colname_1    colname_2    colname_3    colname_4
row_1    1            2             3            5
row_2    4            6             9            1
row_3    2            3             4            2

我計划有一個包含我想要的列列表的文件。

colname_1    colname_3

我是bash腳本的新手,我真的不知道該怎么做。 我看到了其他示例,但是它們都提供了他們預先想要的列號,而我沒有。 抱歉,如果這是重復問題,我嘗試搜索。

我希望結果是

     colname_1     colname_3
row_1    1             3
row_2    2             9
row_3    2             4 

實際上,您可以通過跟蹤與包含列列表的文件中的 名稱匹配的的數組索引來做到這一點。 在數據文件中找到列列表文件中列名稱的數組索引后,您只需讀取數據文件(從第二行開始),然后輸出row_label以及確定的數組索引處的列數據將列列表文件與原始列進行匹配。

可能有幾種方法可以解決此問題,以下假設每列中的數據不包含任何空格。 數組的使用假定為bash(或其他高級shell支持數組),而不是POSIX shell。

該腳本將兩個文件名作為輸入。 第一個是您的原始數據文件。 第二個是您的列列表文件。 一種方法可以是:

#!/bin/bash

declare -a cols  ## array holding original columns from original data file
declare -a csel  ## array holding columns to select (from file 2)
declare -a cpos  ## array holding array indexes of matching columns

cols=( $(head -n 1 "$1") )  ## fill cols from 1st line of data file
csel=( $(< "$2") )          ## read select columns from file 2

## fill column position array
for ((i = 0; i < ${#csel[@]}; i++)); do
    for ((j = 0; j < ${#cols[@]}; j++)); do
        [ "${csel[i]}" = "${cols[j]}" ] && cpos+=( $j )
    done
done

printf " " 
for ((i = 0; i < ${#csel[@]}; i++)); do   ## output header row
    printf "    %s" "${csel[i]}"
done

printf "\n"     ## output newline
unset cols      ## unset cols to reuse in reading lines below

while read -r line; do        ## read each data line in data file 
    cols=( $line )            ## separate into cols array
    printf "%s" "${cols[0]}"  ## output row label
    for ((j = 0; j < ${#cpos[@]}; j++)); do
        [ "$j" -eq "0" ] && { ## handle format for first column
            printf "%5s" "${cols[$((${cpos[j]}+1))]}"
            continue
        }                     ## output remaining columns
        printf "%13s" "${cols[$((${cpos[j]}+1))]}"
    done
    printf "\n"
done < <( tail -n+2 "$1" )

使用示例數據,如下所示:

資料檔案

$ cat dat/col+data.txt
     colname_1    colname_2    colname_3    colname_4
row_1    1            2             3            5
row_2    4            6             9            1
row_3    2            3             4            2

列選擇文件

$ cat dat/col.txt
colname_1    colname_3

使用/輸出示例

$ bash colnum.sh dat/col+data.txt dat/col.txt
     colname_1    colname_3
row_1    1            3
row_2    4            9
row_3    2            4

試試看,如果您有任何疑問,請告訴我。 請注意,bash以處理大型文件的盲目速度而聞名,但只要列列表的長度不可怕,腳本就應該相當快。

Bash在標准命令行實用程序之間的“膠水”效果最佳。 可以編寫循環來讀取海量文件中的每一行,但由於bash並未針對速度進行優化,因此循環速度非常慢。 因此,讓我們看看如何使用一些標准實用程序(grep,tr,剪切和粘貼)來實現此目標。

為簡單起見,讓我們將所需的列標題放入文件中,每行一個。 (您總是可以將制表符分隔的列標題行轉換為這種格式;我們將只使用數據文件的列標題來做到這一點。但是一次只能做一件事。)

$ printf '%s\n' colname_{1,3} > columns
$ cat columns
colname_1
colname_2

printf命令行實用程序的一個重要功能是,它重復其格式,直到用完參數為止。

現在,我們想知道這些列標題中的每個對應於數據文件中的哪一列。 我們可以嘗試將其寫為awk甚至bash中的循環,但是如果將數據文件的標題行轉換為每行一個標題的文件,則可以使用-n選項使用grep告訴我們(在輸出的前面加上匹配項的行號)。

由於列標題是制表符分隔的,因此我們可以使用tr將制表符轉換為換行符,從而將它們轉換為單獨的行:

$ head -n1 giga.dat | tr '\t' '\n'

colname_1
colname_2
colname_3
colname_4

請注意開頭的空白行。 這很重要,因為colname_1實際上對應於第2列,因為行標題位於第1列中。

因此,讓我們查找列名。 在這里,我們將使用幾個grep選項:

  • -F模式參數由幾種模式組成,每行一種,被解釋為普通字符串而不是正則表達式。
  • -x模式必須與整行匹配。
  • -n輸出應以匹配的行號為前綴。

如果我們有Gnu grep ,我們也可以使用-f columns從名為columns的文件中讀取模式。 或者,如果我們使用bash,則可以使用bashism "$(<columns)"將文件的內容作為grep的單個參數插入。 但是現在,我們將保持與Posix兼容:

$ head -n1 giga.dat | tr '\t' '\n' | grep -Fxn "$(cat columns)"
2:colname_1
4:colname_3

好,那很接近。 我們只需要除去行號以外的所有內容即可; 以逗號分隔數字,並在開頭放置1。

 $ { echo 1
 >   grep -Fxn "$(<columns)" < <(head -n1 giga.dat | tr '\t' '\n')
 > } | cut -f1 -d: | paste -sd,
 1,2,4
  • cut -f1選擇字段1。自變量可以是逗號分隔的列表,如cut -f1,2,4
  • cut -d:使用:代替制表符作為字段分隔符(“定界符”)
  • paste -s連接單個文件的行,而不是多個文件的相應行
  • paste -d,使用逗號代替制表符作為字段分隔符。

因此,現在我們需要傳遞參數以cut以選擇所需的列:

$ cut -f"$({ echo 1
>   head -n1 giga.dat | tr '\t' '\n' | grep -Fxn -f columns 
> } | cut -f1 -d: | paste -sd,)" giga.dat
        colname_1       colname_3
row_1   1       3
row_2   4       9
row_3   2       4

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM