次のような複数の列を含むテキストファイルがあります。
1 102.0184 109.5487 107.3716
2 100.2430 107.5874 108.7558
3 103.9029 105.7406 106.9046
4 102.7495 108.1275 104.4731
5 102.8825 105.6664 107.2141
6 104.5323 108.8850 106.0267
7 103.0479 107.3056 105.5108
8 101.2433 108.6113 107.2551
9 104.4821 108.4339 105.9864
10 101.5879 106.3859 102.825
次に、2番目の列のように、次のようにフィルタリングします。
awk '$2<104 {print $1,$2}' file.txt
これを得ました:
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
7 103.0479
8 101.2433
10 101.5879
最初の列の連続した範囲をすべて別のファイルに印刷したいので、この例では3つの出力ファイルがあります。
file_1-5.tmp
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
file_7-8.tmp
7 103.0479
8 101.2433
file_10.tmp
10 101.5879
どうすればいいですか?ファイル内のすべての列に対してこのルーチンをどのように拡張できますか?私はこの問題を複数のファイルに適用したいので、この問題に対する一般的な解決策(この例で提供されている特定の範囲に依存しない)があることを願っています。
答え1
1行awk
コマンド:
awk -v P=-1 '$2<104 {if ($1>P+1)N=$1;P=$1;print $1,$2>"file"N".tmp"}'
結果の提供(ファイル名が気に入らない場合)
==> file1.tmp <==
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
==> file7.tmp <==
7 103.0479
8 101.2433
==> file10.tmp <==
10 101.5879
答え2
以下はawk
プログラムです。パラメーターに指定された列としきい値を使用できます。
awk -v column=2 -v threshold=104 '
function save() { if (lines != "") print lines >"file_" first "-" last ".txt" }
! ($column < threshold) {
save()
first = last = lines = ""
next
}
{
if (first == "") first = $1
last = $1
lines = lines $1 OFS $column ORS
}
END { save() }
'
連続した行は、保存されるまでメモリに残ります。数億の連続行がある場合は、このソリューションを適用する必要があります(各行を一時ファイルに保存し、連続ブロックの最後の行に達すると名前を変更します)。
答え3
awk '{$0 = $2 < 104 ? NR : RS}1' inputfile |
sed -Ee '
$aq
/./,/^$/!d;/./H;$ba;/^$/ba;d;:a
g;s/.//;s/\n.*\n|\n/,/;x;s/.*//;x
s/(.*),(.*)/&w file_\1_\2.tmp/
/,/!s/.*/&w file_&.tmp/
' | ed -s inputfile -
ここではツールを使用しますawk/sed/ed
。Awk
まず、104未満のすべての行に対して行番号を生成します。他の行の場合は、空白行を印刷します。次に、Sed
空でない行から次の空行までの行の範囲を調べます。そして行番号を保持に保存します。これで、n、m、または一般nの2種類の範囲を見つけることができます。これを使用して、ed
これらの範囲をそれぞれn、mw file_n_m.tmp、およびfile_n.tmpに変換する一連のコマンドを作成します。その後、edは動的に生成されたこのedスクリプトを使用して入力ファイルをただちに処理し、その行を.tmpファイルに配置します。
タスクを実行する1つの方法は次のとおりですPerl
。
perl -lane '
BEGIN { $fmt = sprintf q[%s%%s\n%s], (chr 39)x2; }
if ( $F[1] < 104 ) {
push @A, "@F[0,1]";
if ( eof ) {
my $f = join $", q<printf>, $fmt, map(qq[\"$_\"], @A), q[>], (( ! defined $a ) ? qq[file_${.}.tmp] : qq[file_${a}_${.}.tmp]);
system("$f");
} else { $a //= $.; }
} else {
next if ! defined $a;
$b //= $.-1;
my $f = join $", q<printf>, $fmt, map(qq[\"$_\"], @A), q[>], (( $a == $b ) ? qq[file_$b.tmp] : qq[file_${a}_$b.tmp]);
system("$f");
($a, $b, @A) = (undef)x2;
}
' yourfile
結果:
file_1_5.tmp file_7_8.tmp file_10.tmp
入力が与えられると、次の3つのファイルが生成されます。
%もっとfile_1_5.tmp file_7_8.tmp file_10.tmp
::::::::::::::
file_1_5.tmp
::::::::::::::
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
::::::::::::::
file_7_8.tmp
::::::::::::::
7 103.0479
8 101.2433
::::::::::::::
file_10.tmp
::::::::::::::
10 101.5879
説明する:
まず、基本的なトップレベルのアイデア:2番目のフィールドが数字104の後ろにあるかどうかに注意深く注意してください。この場合、以前の範囲を印刷する必要があります。単一の長さの範囲では、ファイル名が反映されるように変更されます。
eof
別のケースは、現在の範囲($ F [1] < 104)を累積する場合です。この作業中にヒットすると、範囲を印刷する必要があることに注意してください。
PS:このsystem
コマンドは動的に生成された形式を使用して動的に生成され、そのデータは最初と2番目のフィールドであり、最後に範囲に基づいて.tmpファイル名が生成されます。
$a
$b
範囲の開始/終了行番号です。彼らの州は私たちに正しい決定を下すように伝えます。