2000列のLinuxシステムの大容量ファイルから複数の特定の列をインポートしたいと思います。どうすればいいですか?
file1.gz ファイルは次のようになります。
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 ...
file2にインポートする必要がある列は次のとおりです。
186
187
188
189
190
191
192
193
194
195
(about 1000 column)
ベストアンサー1
では、awk
特定の列を番号で参照できます。たとえば、列12$12
は列1345です$1345
。また、デフォルトの列区切り文字は空白なので、スペースで区切られたファイルを使用する例では、解凍して目的の列をawk
印刷するスクリプトを渡すだけです。
zcat file.gz | awk '{print $1,$12,$195} > newFile
ここで複雑なのは、必要な列が多すぎて印刷できないことです。ここでは、まず列を読んでから印刷する必要があります。
awk '{
if (NR==FNR){ wantedColumns[NR]=$1 }
else{
for(i=1;i<=length(wantedColumns)-1;i++){
printf "%s ", $(wantedColumns[i])
}
print $(wantedColumns[length(wantedColumns)])
}
}' file2 <(zcat file1.gz)
たとえば、
$ zcat file1.gz
line1_field1 line1_field2 line1_field3 line1_field4 line1_field5 line1_field6
line2_field1 line2_field2 line2_field3 line2_field4 line2_field5 line2_field6
line3_field1 line3_field2 line3_field3 line3_field4 line3_field5 line3_field6
line4_field1 line4_field2 line4_field3 line4_field4 line4_field5 line4_field6
line5_field1 line5_field2 line5_field3 line5_field4 line5_field5 line5_field6
line6_field1 line6_field2 line6_field3 line6_field4 line6_field5 line6_field6
line7_field1 line7_field2 line7_field3 line7_field4 line7_field5 line7_field6
line8_field1 line8_field2 line8_field3 line8_field4 line8_field5 line8_field6
line9_field1 line9_field2 line9_field3 line9_field4 line9_field5 line9_field6
$ cat file2
2
4
5
このファイルに対して上記のスクリプトを実行すると、次のような結果が得られます。
$ awk '{
> if (NR==FNR){ wantedColumns[NR]=$1 }
> else{
> for(i=1;i<=length(wantedColumns)-1;i++){
> printf "%s ", $(wantedColumns[i])
> }
> print $(wantedColumns[length(wantedColumns)])
> }
> }' file2 <(zcat file1.gz)
line1_field2 line1_field4 line1_field5
line2_field2 line2_field4 line2_field5
line3_field2 line3_field4 line3_field5
line4_field2 line4_field4 line4_field5
line5_field2 line5_field4 line5_field5
line6_field2 line6_field4 line6_field5
line7_field2 line7_field4 line7_field5
line8_field2 line8_field4 line8_field5
line9_field2 line9_field4 line9_field5
line10_field2 line10_field4 line10_field5
説明する
if (NR==FNR){ wantedColumns[NR]=$1 }
:NR
は入力行番号、FNR
は行番号現在のファイルの。 2 つは、最初のファイルを読み取る場合にのみ同じです。したがってNR
、等しい場合はFNR
最初のファイルを読み取ると、そのファイルの最初のフィールドはwantedColumns
インデックスが行番号で値がフィールドの配列に格納されます。else { ... }
:私たちのラーメンいいえ最初のファイルを読み込んでいます(今度は2番目のファイルにある場合)。for(i=1;i<=length(wantedColumns)-1;i++){
NR
:最初のインデックス(上のループで使用した値のために1です)から2番目のインデックスまで、目的の列の配列を繰り返し、各列を印刷してスペースを入れます。重要な警告:必ずしも元のファイルの列の順序を維持するわけではありません。列はの順に印刷されますfile2
。この順序が元のファイルの順序と異なる場合file2
(たとえば、1 3 2
代わりに存在する場合1 2 3
)は、この順序で印刷されます。print $(wantedColumns[length(wantedColumns)])
:最後のフィールドを印刷し、その後に改行文字が続きます。<(zcat file1.gz)
bash
:これは、コマンドの出力をファイルとして処理できるようにする["プロセス置換"][1]という機能(他の一部のシェルと同様)です。ここでは解凍されたファイルを使用し、zcat
それを2番目の入力「ファイル」にawk
。
この方法では、各行の末尾に余分なスペースを追加します。これが問題の場合は、sed
最後に配管することで回避できます。
awk '...' | sed 's/ $//'
または、カンマで区切られたフィールドのリストにcut
変更して、次のように渡すこともできます。file2
cut
$ zcat file1.gz | cut -d' ' -f $(tr '\n' ',' < file2 | sed 's/,$//')
line1_field2 line1_field4 line1_field5
line2_field2 line2_field4 line2_field5
line3_field2 line3_field4 line3_field5
line4_field2 line4_field4 line4_field5
line5_field2 line5_field4 line5_field5
line6_field2 line6_field4 line6_field5
line7_field2 line7_field4 line7_field5
line8_field2 line8_field4 line8_field5
line9_field2 line9_field4 line9_field5
line10_field2 line10_field4 line10_field5
説明する
zcat file1.gz |
:解凍し、file1.gz
内容を次のコマンドに渡します。cut -d' '
cut
:デフォルトタブ()の代わりにスペースを\t
フィールド区切り文字として使用することを意味します。-f $(tr '\n' ',' < file2 | sed 's/,$//')
:印刷するフィールドを-f
表示します。cut
カンマで区切られたフィールドリストを使用できるため、tr '\n' ','
すべての改行をコンマに変換して結果をフィールドリストに渡します。 [1]:https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html