最初の行の情報に基づいて、ファイルを複数のファイルに分割したいと思います。たとえば、次のようになります。
入力する:
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 30 30 30 30
0 2 2 0 2 0 2 0 2 0 2 2 0 0 2 2 2 0 1 1 1 2 0 2 0 0 0 2 0 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 0 2 0 2
希望の出力:
output1.txt
02202020
02101011
02101011
output2.txt
2022002
1022002
1022002
output3.txt
220111
220000
220000
output4.txt
202000200202
202001200202
202001200202
出力30.txt
0202
0202
0202
答え1
$ awk '
NR == 1 {
for (i=1; i<=NF; i++) {
output[i] = "output" $i ".txt"
files[output[i]] = 1
}
next
}
{
for (i=1; i<=NF; i++) printf "%s", $i > output[i]
for (file in files) print "" > file
}
' input.filename
$ for f in output*.txt; do echo $f; cat $f; done
output1.txt
02202020
02101011
02101011
output2.txt
2022002
1022002
1022002
output3.txt
220111
220000
220000
output30.txt
00202
00202
00202
output4.txt
2020002
2020012
2020012
ヘッダー行には32個のフィールドがあり、他の行には33個のフィールドがあります。この問題を最初に解決する必要があります。
答え2
Perlスクリプト。
$in
代わりにファイル名を設定するgenome.txt
か、名前を引数として渡します。
スクリプト名を指定しcounter.pl
て実行権限を付与してから実行してください。./counter.pl
chmod 755 counter.pl
./counter.pl
または
chmod 755 counter.pl
./counter.pl genome.txt
counter.pl:
#!/usr/bin/perl
use strict;
use warnings;
my $in = $ARGV[0] || 'genome.txt'; # input file name
open (my $F, '<', $in) or die "Cannot open input file $!";
my $n = 0;
my %fd = ();
my @fd = ();
while (<$F>) {
# trim
s/^\s+//;
s/\s+$//;
next if (!$_); # Skip empty lines
my @x = split(/\s+/, $_);
# 1st line, open files
if ( ! $n++) {
my $fd = 0;
for (@x) {
open ($fd{$_}, '>', "output$_.txt")
or die ("Cannot open file $!")
if (!exists($fd{$_}));
$fd[$fd++] = $_;
}
}
else { # Write data
die ("Should have " . ($#fd+1) . " entries on line $n")
if ($#x != $#fd);
for (0 .. $#x) {
print {$fd{$fd[$_]}} ($x[$_]);
}
print {$fd{$_}} ("\n") for (keys %fd);
}
}
close $fd{$_} for (keys %fd);
close $F;
# the end
1行あたりの固定語数(時々32、例えば33)。
このバージョンはすべての列バリアントに対応できますが、すべての行の単語数が同じでなければなりません。die
単語数が異なる場合やファイルを開くことができない場合は、エラー(行)が表示されます。
ファイル名($in
)を調整するだけです。
入力ファイル:(最後に追加0を削除)
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 30 30 30 30
0 2 2 0 2 0 2 0 2 0 2 2 0 0 2 2 2 0 1 1 1 2 0 2 0 0 0 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2
出力1.txt
02202020
02101011
02101011
出力2.txt
2022002
1022002
1022002
出力30.txt
0202
0202
0202
出力3.txt
220111
220000
220000
出力4.txt
2020002
2020012
2020012
答え3
さて、楽しいです。組み込み機能に大きく依存する純粋なBashバージョン(要求時)です。読む単語を配列に送信してファイルに保存します。ファイルは output001.txt ....output030.txt 形式になっています。 @ringOが修正したデータファイルを使用してテストしました。テストされていませんが、非常に大きなファイルでは、他のファイルよりも時間とリソースを節約できます。
データ:
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 30 30 30 30
0 2 2 0 2 0 2 0 2 0 2 2 0 0 2 2 2 0 1 1 1 2 0 2 0 0 0 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2
0 2 1 0 1 0 1 1 1 0 2 2 0 0 2 2 2 0 0 0 0 2 0 2 0 0 1 2 0 2 0 2
源泉:
#!/usr/bin/env bash
# genome : to sort genome data sets according to patterns of the first (header)
# line of the file. Data must be space delimited. No dependencies.
#
# Usage:
#
# ./genome "data.txt"
# global arrays
sc=( ) # array of set element counts
sn=( ) # array of set id numbers
# output_file "set id"
# change the output pattern and digit output width as required - default
# pattern is output.txt and digit width of three : output000.txt
output_file(){
# format concept: pattern000.txt
local op='output.txt' # output pattern
local ow=3 # output width: 3 => 000
printf "%s%0${ow}d.%s" "${op%%.*}" "$1" "${op##*.}"
}
# define_sets "input.txt"
# identify sets - get elements count and sets id numbers from file
# header.
define_sets(){
# declare and initialize
local a an b c n
read -r c < "$1"
read -r a b <<< "$c"
n=0; sn=( $a )
# recurse header, identify sets
until [[ -z $b ]]
do
n=$((n+1))
an=$a
read -r a b <<< "$b"
[[ $an == $a ]] || { sn+=( $a ); sc+=( $n ); n=0; }
done
n=$((n+1))
sc+=( $n )
}
# reset_files
# optional function, clears file data, otherwise data is appended to existing
# output files.
reset_files(){
for s in ${sn[@]}
do
> "$(output_file "$s")"
done
}
# extract_data "input.txt"
# use defined sets to extract data from the input file and send it to required
# output files. Uses nested 'while read' to bypass file header as data is saved.
extract_data(){
local a c n s fn da=( )
while read -a da
do
while read -a da
do
a=0 n=0
for s in ${sc[@]}
do
c="$(echo "${da[@]:$a:$s}")" # words => string
echo "${c// /}" >> "$(output_file "${sn[$n]}")" # save
n=$((n+1))
a=$((a+s))
done
done
done < "$1"
}
define_sets "$1" # get data set structure from header
reset_files # optional, clears and resets files
extract_data "$1" # get data from input file and save
# end file
データ出力:
$ cat output001.txt
02202020
02101011
02101011
$ cat output002.txt
2022002
1022002
1022002
$ cat output003.txt
220111
220000
220000
$ cat output004.txt
2020002
2020012
2020012
$ cat output030.txt
0202
0202
0202
答え4
楽しみにして他の解決策もあります。
awk '{ for (i=1; i<=NF;i++){
if (NR==1) { file[i]=$i }
if (NR!=1) { f="output" file[i] ".txt";
g="output" file[i+1] ".txt";
printf("%s%s",$i,f==g?OFS:ORS)>>f;
close(f);
}
}
}' file
無制限のフィールドが必要な場合に変更し?OFS:
てください?"":
。
ペアのない値を受け取るデフォルトファイルはですoutput.txt
。このファイルは、最初の行の列数が処理される次の行と一致しない場合に値を受け取ります。すべてが正しい場合は空でなければなりません。スクリプトを実行した後もまだ存在する場合は、どこかに問題があります。