たとえば、複数のヘッダーを持つ複数のファイルを、すべての情報を含む1つのファイルにリンクしたいとします。
ファイル1:
Numbers
1
2
3
Letters
A
B
C
ファイル2:
Numbers
4
5
6
Letters
D
E
F
ファイル3は、次のものをマージすることによって生成されます。
Numbers
1
2
3
4
5
6
Letters
A
B
C
D
E
F
これまでcatを試してfile2を最後に追加し、各ヘッダーの下のすべてのデータをgrepしようとしましたが、簡単に言えば失敗しました。
貼り付けようとしましたが、各ファイルのデータ量が等しい場合は、最後のファイルの横に[ファイル固有の情報]タブを追加するので、悪くはありませんが、あるファイルのヘッダーの下に他のファイルよりも多くの項目がある場合は混乱を招きます。
この種の問題を解決する方法を知っている人はいますか?
答え1
セクションキーワードの下に「====」があると仮定すると、次のPythonはセクション名を明示的に指定せずに問題を解決します。
import sys
from collections import OrderedDict
combined = OrderedDict()
seperator = '===='
for file_name in 'f1.txt f2.txt'.split(): #sys.argv[1:]:
with open(file_name) as fp:
lines = fp.readlines()
data = []
while len(lines):
# reverse over the lines
line = lines.pop(-1)
if not line.strip(): continue # skip empty
if line.startswith(seperator):
name = lines.pop(-1)
section = combined.setdefault(name, [])
section.extend(reversed(data))
data = []
else:
data.append(line)
for idx, k in enumerate(reversed(combined)):
if idx != 0:
print # insert empty line before all but first
sys.stdout.write(k)
print('=' * len(k))
for line in combined[k]:
sys.stdout.write(line)
コマンドラインを呼び出すときにファイル名を指定する必要があります。
これにより、次のような出力が生成されます。
Numbers
========
1
2
3
4
5
6
Letters
========
A
B
C
D
E
F
答え2
Perlスクリプトを使用してこれを行う1つの方法は次のとおりです。
#!/usr/bin/perl
my @fileInfo;
open(my $fh, "<file1");
push (@fileInfo,<$fh>);
close($fh);
open(my $fh, "<file2");
push (@fileInfo,<$fh>);
close($fh);
my @letLines;
my @numLines;
my $numMode = 0;
my $letMode = 0;
foreach my $line (@fileInfo) {
# skip empty and '==' lines
next if ($line =~ /^$/);
next if ($line =~ /==/);
if ($line =~ /Letters/) {
$letMode = 1;
$numMode = 0;
next;
}
if ($line =~ /Numbers/) {
$numMode = 1;
$letMode = 0;
next;
}
if ($letMode) {
push (@letLines, $line);
next;
}
if ($numMode) {
push (@numLines, $line);
next;
}
}
print "Numbers\n";
print "=======\n";
print @numLines;
print "\n";
print "Letters\n";
print "=======\n";
print @letLines;
# vim: set ts=2 nolist :
出力
Numbers
=======
1
2
3
4
5
6
Letters
=======
A
B
C
D
E
F
答え3
あなたが提供した入力のnumberletters.sh:
#!/bin/bash
echo Numbers
for f in "$@"
do
grep -E '^[0-9]+$' "$f"
done
echo
echo Letters
for f in "$@"
do
grep -E '^[A-Z]+$' "$f"
done
echo
numbersletters.sh file1 file2
返品
Numbers
1
2
3
4
5
6
Letters
A
B
C
D
E
F
| sort -u
for for ループを追加すると、ソートされ、一意になります。
答え4
以下は、任意のヘッダー名で動作する単純なPerlソリューションです。
#!/usr/bin/env perl
my ($last,$head);
my %k; ## this will hold our data
my $separator="=====";
my @lines=<>; ## Read all lines
for(my $i=0; $i<=$#lines; $i++){ ## for each line...
next if $lines[$i]=~/$separator/; ##skip the separators
next if $lines[$i]=~/^\s*$/; ##skip empty lines
if ($lines[$i+1]=~/====/) { ## if the NEXT line is a separator,
$head=$lines[$i]; ## the this line is a heading
next;
}
else { ## save the contents of this group into the %k hash.
push @{$k{$head}},$lines[$i];
}
}
## Now print everything
foreach (keys(%k)) {
print "$_========\n", @{$k{$_}};
}