3~5개의 .csv 파일이 있고 모든 것을 자체 열에 유지하면서 함께 병합해야 합니다. 아래는 파일의 행 수가 다른 간단한 예입니다. 파일1 파일2 파일3 파일4 파일5 > 최종파일.
파일 1
1 1
1 1
1 1
파일 2
2 2 2
2 2 2
파일 3
3
3
3
3
파일 4
4
4
파일 5
5
5
5
5
5
모든 파일을 병합하고 모든 것을 자체 열에 유지하려면 .csv 파일의 결과가 필요합니다. 내 예에서 0은 빈 셀/열입니다.
최종 문서
1 1 2 2 2 3 4 5
1 1 2 2 2 3 4 5
1 1 0 0 0 3 0 5
0 0 0 0 0 3 0 5
0 0 0 0 0 0 0 5
현재 시도하고 있는 모든 작업은 해당 셀/열에 데이터가 없으면 모든 항목을 왼쪽으로 밀어냅니다.
최종 문서
1 1 2 2 2 3 4 5
1 1 2 2 2 3 4 5
1 1 3 5
3 5
5
답변1
csvkit
헤더를 사용하지 말고 -H
추론을 비활성화하세요 -I
. 그렇지 않으면 값이 다음 1
과 같이 해석됩니다.TRUE
csvjoin -H -I file*
지금까지 (헤더가 없어서 오류 메시지가 있어서 불평하고 추가합니다)
a,b,a2,b2,c,a2_2,a2_3,a2_4
1,1,2,2,2,3,4,5
1,1,2,2,2,3,4,5
1,1,,,,3,,5
,,,,,3,,5
,,,,,,,5
누락된 값을 대체하고 구분 기호를 잃는 방법은 취향의 문제이지만 의 필드를 반복하고 awk
구분 기호를 로 설정하고 -F","
헤더를 건너뛰고 변환 필드를 NR>1
추가할 수 있습니다.0
NULL
csvjoin -H -I file* | awk -F"," 'NR>1{for (i=1; i<=NF; i++) printf ("%s ", $i+0); print""}'
이것은 당신이 원하는 곳으로 갈 것입니다
1 1 2 2 2 3 4 5
1 1 2 2 2 3 4 5
1 1 0 0 0 3 0 5
0 0 0 0 0 3 0 5
0 0 0 0 0 0 0 5
또는 순수 awk
버전은 각 파일의 필드 수가 일정하다고 가정합니다.
awk '{for (i=1; i<=NF; i++) {mx[FNR][nf+i]=$i}}
ENDFILE{nf+=NF; nor=(nor<FNR)?FNR:nor}
END{for (i=1;i<=nor;i++) {for (j=1;j<=nf;j++) printf ("%s ", mx[i][j]+0); print ""}}' file*
단지 각 파일의 값을 배열로 로드하는 것뿐입니다.mx[][]
{for (i=1; i<=NF; i++) {mx[FNR][nf+i]=$i}}
각 파일의 끝에서 열 인덱스를 nf
파일의 필드 수 만큼 오른쪽으로 이동하고 현재 레코드 수나 마지막 행렬의 크기 중 더 큰 값을 NF
취합니다.NR
nor
ENDFILE{nf+=NF; nor=(nor<FNR)?FNR:nor}
마지막으로 행렬 차원을 반복하고 NULL
값을 변환합니다.
END{for (i=1;i<=nor;i++) {for (j=1;j<=nf;j++) printf ("$s ", mx[i][j]+0); print ""}}'
답변2
% stitch --autocol --ofs="\\t" one two three four five
1 1 2 2 2 3 4 5
1 1 2 2 2 3 4 5
1 1 3 5
3 5
5
가깝지만 paste
아직은 거기까지 도달하지 못했습니다. 실제 CSV 데이터를 설정 --ofs=,
하고 --ifs=,
얻으려면 쉼표로 분할하는 것이 매우 나쁜 CSV 파서라는 점에 유의하십시오.
#!/usr/bin/env perl
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
GetOptions(
'autocol|ac' => \my $Flag_AutoCol,
'ifs=s' => \my $Flag_IFS,
'ofs=s' => \my $Flag_OFS,
'rs=s' => \my $Flag_RS,
) or exit 64;
$Flag_IFS //= ' ';
$Flag_OFS //= ' ';
$Flag_RS //= '\n';
$Flag_IFS =~ s/(\\t)/qq!"$1"!/eeg;
$Flag_OFS =~ s/(\\[nrt])/qq!"$1"!/eeg;
$Flag_RS =~ s/(\\[nrt])/qq!"$1"!/eeg;
my @fhs;
my $seen_stdin = 0;
for my $arg (@ARGV) {
# "file" (no spec) or "file:" (no spec but colon) or "file:spec"
# where no spec means "print all columns and do not preserve column
# positions as will not try to guess that"
my ( $file, $spec );
if ( $arg =~ m/^([^:]+)$/ ) {
$file = $1;
} elsif ( $arg =~ m/^(.+):([^:]*)$/ ) {
$file = $1;
$spec = $2;
}
die "could not parse file from '$arg'\n" if !defined $file;
my $fh;
if ( $file eq '-' and !$seen_stdin ) {
$fh = \*STDIN;
$seen_stdin = 1;
} else {
open $fh, '<', $file or die "could not open $file: $!\n";
}
push @fhs, [ $fh, defined $spec ? specify($spec) : undef ];
}
my $have_fhs = @fhs;
while ($have_fhs) {
my $pad_col = 0;
for my $i ( 0 .. $#fhs ) {
if ( defined $fhs[$i]->[0] ) {
my $line = readline $fhs[$i]->[0];
if ( !defined $line ) {
# EOF on an input file
$fhs[$i]->[0] = undef;
$have_fhs--;
$pad_col += @{ $fhs[$i]->[1] } if defined $fhs[$i]->[1];
next;
}
# Complicated due to not wanting to print the empty columns if
# there's nothing else on the line to print (works around getting
# an ultimate blank line that messes up the shell prompt)
if ($pad_col) {
print( ($Flag_OFS) x $pad_col );
$pad_col = 0;
}
chomp $line;
my @fields = split /$Flag_IFS/, $line;
# Set field count from the first line of input (may cause
# subsequent uninit warnings if the number of columns then drops)
if ( $Flag_AutoCol and !defined $fhs[$i]->[1] ) {
$fhs[$i]->[1] = [ 0 .. $#fields ];
}
if ( defined $fhs[$i]->[1] ) {
print join( $Flag_OFS, @fields[ @{ $fhs[$i]->[1] } ] );
} else {
print join( $Flag_OFS, @fields );
}
print $Flag_OFS if $i != $#fhs;
} elsif ( defined $fhs[$i]->[1] ) {
$pad_col += @{ $fhs[$i]->[1] };
}
}
print $Flag_RS if $have_fhs;
}
exit 0;
# Parse 1,2,3,5..9 type input into Perl array indices
sub specify {
my $spec = shift;
my @indices;
SPEC: {
if ( $spec =~ m/\G(\d+)\.\.(\d+),?/cg ) {
push @indices, $1 .. $2;
redo SPEC;
}
if ( $spec =~ m/\G(\d+),?/cg ) {
push @indices, $1;
redo SPEC;
}
if ( $spec =~ m/\G(.)/cg ) {
warn "unknown character '$1' in spec '$spec'\n";
exit 65;
}
}
# Assume user will use awk- or cut-like column numbers from 1, shift
# these to perl count-from-zero internally.
$_-- for @indices;
return \@indices;
}
__END__
=head1 NAME
stitch - joins columns from multiple input files
=head1 SYNOPSIS
$ cat a
a b c
$ cat b
1 2 3
4 5 6
7 8 9
$ stitch --ofs=\\t a:2 b:1,3
b 1 3
4 6
7 9
That is, column two from the first file, and columns one and three from
the second. The range operator C<..> may also be used to select a range
of columns, e.g. C<1,4..6,8>.
=head1 DESCRIPTION
This program joins columns by line number from multiple input files.
=head1 USAGE
$ stitch [--ac] [--ifs=s] [--ofs=s] [--rs=s] file[:spec] [file[:spec] ..]
Use C<-> to select columns from standard input; otherwise, specify files
to read input from, along with the optional column specification (by
default, all columns will be selected).
This program supports the following command line switches:
=over 4
=item B<--autocol> | B<--ac>
Set the number of columns from the first line of input seen from a
C<file> if a column specification was not provided for said C<file>.
Influences empty field padding (which only happens with a column
specification should a file run short before the others).
=item B<--ifs>=I<s>
Specify the input field separator (space by default). A C<\t> will be
expanded to the actual character:
$ perl -E 'say join("\t", qw/a b c/)' | stitch --ifs=\\t -- -:2
Or, use a regex:
$ perl -E 'say join("\t", qw/a b c/)' | stitch --ifs='\s+' -- -:2
=item B<--ofs>=I<s>
Output field separator (space by default). Similar expansion done as
for B<--ifs>, though also C<\n> and C<\r> are allowed.
=item B<--rs>=I<s>
Output record separator (newline by default). Expansion done as
for B<--ofs>.
=back
=head1 SECURITY
Probably should not be run under elevated privs due to user-supplied
input to the L<perlfunc/"split"> function.
Passing a user-supplied regex to L<perlfunc/"split"> might be a bit
sketchy especially if L<sudo(1)> or the like is involved. It might be
nice to have per-file IFS (so one could split on spaces on stdin, and
C<:> from C<passwd>), but that would add complications.
=head1 SEE ALSO
awk(1), comm(1), cut(1), join(1), perl(1)
=cut