두 개의 파일이 있는 다음과 같은 상황이 있습니다.
파일 1
not relevant = does not matter
some stuff
# var1=1
# var 2 = 2
# var3 = 3
some stuff
파일 2
some other stuff
# does not matter either
# var1=a
# var 2 = b
# var3 = c
some other stuff
#
bash 스크립트를 사용하여 해당 변수와 해당 값으로 시작 하고 포함하는 모든 줄에서 추출 =
하고 모든 것을 수집하여 새 파일에 쓰고 싶습니다
var1,var 2,var3
1,2,3
a,b,c
변수 이름에는 공백이 포함될 수 있습니다. =
앞뒤에 공백이 있을 수도 있습니다. 오른쪽 값에는 공백이 포함되어 있지 않습니다. 공백이 나타나면 두 파일 모두에 나타납니다.
답변1
이는 쉘 스크립트가 아닌 텍스트 처리 언어(예: awk 또는 Perl)가 필요한 작업입니다.
$ cat vars2csv.pl
#!/usr/bin/perl
use strict;
# %vars is a Hash-of-Hashes (HoH) where the primary keys
# are the filenames, and each element is a hash containing
# each "variable" name found in in the input and its
# corresponding value. See man pages for perldata and perldsc.
my %vars;
# Array @fields and hash %seen to keep track of new
# "variable" names in the order we see them.
my @fields;
my %seen;
# Keep a copy of the arguments so we can output the data in
# the same order we read them.
my @files = @ARGV;
while (<>) {
chomp;
next unless /^#.*=/;
s/^#\s*//;
my ($key,$val) = split /\s*=\s*/, $_, 2;
if (!defined($seen{$key})) {
push @fields, $key;
$seen{$key} = 1;
};
# $ARGV is the name of the current file being read
# by the `while(<>)` loop.
$vars{$ARGV}{$key} = $val;
};
print join(",", @fields), "\n";
foreach my $f (@files) {
next unless -r $f; # skip output for filenames that weren't readable
print join(",", @{$vars{$f}}{@fields}), "\n";
};
스크립트는 파일을 읽는 순서와 필드 이름을 보는 순서를 추적합니다. 왜냐하면 Perl 해시는 본질적으로 순서가 없기 때문입니다(이는 대부분의 언어에서 대부분의 연관 배열 구현에서 일반적입니다). 출력 단계에서 키를 정렬하도록 작성할 수 있으므로(perl에는 매우 유용한 내장 sort
기능이 있습니다) 최소한 예측 가능한 순서로 출력될 것이지만, 몇 가지 변수를 사용하여 기억하는 것이 더 나을 것이라고 생각합니다. 원래 주문.
여러 출력 필드에서 작동하며 필드 이름이나 값이 무엇인지 상관하지 않습니다. 일치하는 줄에서 =
선행 공백 뒤와 첫 번째 기호 앞의 모든 내용은 "키"이고 =
첫 번째 기호 뒤의 모든 내용은 값입니다. 주변 공간은 =
키나 값에 포함되지 않습니다( \s*=\s*
단지 행이 분할되지 않음 =
). perldoc -f split
분할 기능에 대한 자세한 내용은 을 참조하십시오.
특정 키가 파일에 여러 번 나타나면 마지막으로 발생한 값이 값 출력이 됩니다. 첫 번째 이벤트를 유지하고 후속 이벤트를 무시하려면 다음 줄을 추가하세요.앞으로은행 $vars{$ARGV}{$key} = $val;
:
next if (defined($vars{$ARGV}{$key}));
실행 예시:
$ chmod +x ./vars2csv.pl
$ ./vars2csv.pl file1 file2
var1,var 2,var3
1,2,3
a,b,c
주목할 만한 점: 이 스크립트는 a로 시작하지 않고 다음을 #
포함하는 모든 항목을 무시합니다 =
. 즉, 을 처리합니다.모두이 조건과 일치하는 줄 - =
정의하지 않으려는 변수를 포함하는 주석 처리된 줄을 포함합니다. 입력 파일의 내용에 따라 이는 수정해야 할 버그일 수 있습니다(원치 않는 줄을 제외하는 패턴을 찾아내거나 원하는 줄만 일치하는 더 나은 패턴을 고안하여).
그런데 next unless -r $f;
존재하지 않는 파일 이름 매개 변수와 읽기를 차단하는 권한을 사용하여 스크립트를 테스트했기 때문에 해당 줄을 스크립트에 추가했습니다. 이러한 오류가 발생하면 Perl은 경고 메시지를 인쇄하지만 스크립트는 쉼표로 구분된 빈 필드가 포함된 줄을 인쇄합니다. 이 줄은 이 출력을 방지합니다.
스크립트는 또한 쉼표로 구분된 빈 필드 줄을 인쇄합니다.읽을 수 있는설명 이 포함되지 않은 파일입니다 var=value
. 이러한 파일의 출력도 방지하려면 다음을 추가하십시오.앞으로와이어 print join...
.
next unless (keys %{ $vars{$f} }); # skip output for files with NO key=val comments
일부 필드가 포함되어 있지만 전부는 아닌 파일은 해당 필드에 대해서는 올바른 값을 인쇄하고 누락된 필드에 대해서는 null 값을 인쇄합니다. 예를 들어, 포함된 파일만 출력 라인으로 # var1=1
인쇄됩니다 . 1,,
이 파일의 출력을 건너뛰려면 다음을 수행하세요.
next unless (@{$vars{$f}}{@fields}); # skip output for files missing ANY key
답변2
awk
모든 쉘에서 사용:
#!/usr/bin/awk -f
BEGIN {FS = " ?= ?" ; OFS="," ;}
NF == 2 && /^#/ {
sub(/^# /, "", $1)
if (FILENAME != oldFileName) {
files[filesCnt++] = FILENAME
oldFileName = FILENAME
}
hdrYetFoundIdx = -1
for (i = 0; i < hdrCnt; i++) {
if (hdr[i] == $1) {
hdrYetFoundIdx = i
break
}
}
if (hdrYetFoundIdx == -1) hdr[hdrCnt++] = $1
val[files[filesCnt-1],$1] = $2
}
END {
for (i = 0; i < hdrCnt; i++)
printf "%s%s", hdr[i], ((i<hdrCnt-1)?OFS:ORS)
for (i = 0; i < filesCnt; i++)
for (j = 0; j < hdrCnt; j++)
printf "%s%s", val[files[i],hdr[j]], ((j<hdrCnt-1)?OFS:ORS)
}
줄 시작 부분 사이에 공백이 있을 수 있는 경우 처리 코드의 #
조건 awk
은 다음과 같아야 하며 NF == 2 && /^ *#/
호출은 sub
다음과 같습니다.sub(/^ *#/, "", $1)
답변3
첫 번째 파일이 얻을 변수 세트를 정의한다고 가정합니다.
#!/usr/bin/perl
use strict;
$/=undef; # no input register separator
$,="|"; # ouput field separator
$\="\n"; # ouput register separa
my (%pair,@var);
while(<>){ # for each file
my %pair= (m/#\s*(\S.*?)\s*=\s*(.*)/g); # get the (var->value) pairs
if(not @var){
@var = keys(%pair); # get and print schema
print( @var );
}
print( @pair{@var} ); # print the values
}
답변4
모든 파일의 모든 레코드에 동일한 세 가지 변수가 동일한 순서로 할당된다고 가정합니다 pcregrep
.
assign='#.*?=\h*(.*?)\h*'
pcregrep -hMo1 -o2 -o3 --om-separator=, "^$assign\n$assign\n$assign\$" file1 file2
그것은 당신에게 가치를 줄 것입니다. 헤더의 경우 첫 번째 파일의 첫 번째 레코드에서 추출할 수 있습니다.
assign='#\h*(.*?)\h*=.*'
pcregrep -Mo1 -o2 -o3 --om-separator=, "^$assign\n$assign\n$assign\$" file1 | head -n1