첫 번째 줄의 정보를 기반으로 파일을 여러 파일로 분할하고 싶습니다. 예를 들어 다음과 같습니다.
입력하다:
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
펄 스크립트.
$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
줄당 고정 단어 수(때때로 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
. 이 파일은 첫 번째 행의 열 수가 처리되는 다음 행과 일치하지 않는 경우 값을 받습니다. 모든 것이 정확하다면 비어 있어야 합니다. 스크립트를 실행한 후에도 여전히 존재한다면 어딘가에 문제가 있는 것입니다.