Bash에서 일치하는 패턴을 기반으로 줄 분할

Bash에서 일치하는 패턴을 기반으로 줄 분할

두 개의 파일이 있는 다음과 같은 상황이 있습니다.

파일 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

관련 정보