2つのファイルがあり、ファイル1の単語がファイル2にない場合は、新しいファイル(ファイル3など)の対応する行にfalseという単語を生成したいと思います。それ以外の場合は、その行にtrueを出力したいと思います。
ファイル1:
a
b
c
d
ファイル2:
a
d
c
e
t
y
ファイル3:
true
false
true
true
awk/sed/grepコマンドを使用してこれを行う方法はありますか?
答え1
file2が空ではなく、問題を引き起こさず、メモリに入るほど大きくないとします。
awk 'NR==FNR{a[$0]; next} {print ($0 in a ? "true" : "false")}' file2 file1
答え2
man grep bash
UNTESTED などの操作を読み、実行します。
for pat in $(cat "file 1") ; do
ans="False"
grep --quiet "^$pat\$" "file 2" || \
ans="True"
echo -e "$pat\t$ans" >>"file 3"
答え3
$ perl -le '
# construct a partial regex from the first filename argument
my $re = join("|", split /\n+/, do { local(@ARGV,$/) = shift; <> });
# complete and pre-compile the regex
$re = qr/\b(?:$re)\b/;
# read and process stdin and/or remaining filename args
while(<>) {
print /$re/ ? "true" : "false"
}' file2 file1
true
false
true
true
このPerlスクリプトは、最初の引数()で表されるファイル全体を読み取り、ファイルの各行の各単語にfile2
一致する正規表現を構成します。正規表現の各単語は交互に区切ります|
。ファイルには1行に1つの単語が含まれ、行は1つ以上の改行で区切られていると仮定します(これは空行を無視する有用な副作用があります)。
注:の各単語はfile2
正規表現として解釈されます。固定文字列として解釈するには、行をmy $re ...
次のように変更します。
my $re = join("|", map { quotemeta $_ } split /\n+/, do { local(@ARGV,$/) = shift; <> });
quotemeta関数は、文字列内のすべての正規表現メタ文字を「引用」して、特別な意味を失い、リテラル文字として扱われるようにします。望むよりperldoc -f quotemeta
。この関数は、返されたリストの各要素にブロックが適用されるようmap
にします。とを参照してください。{ quotemeta $_ }
split
perldoc -f map
perldoc -f split
ちなみに、do { local(@ARGV,$/) = shift; <> })
これはファイルを「slurping」するために使用される非常に一般的なPerl慣用語です。つまり、ファイル全体を一度に読み取ることです。以下を含む同じことを行う他の多くの方法があります。ファイル::喫煙者しかし、これはシンプルで移植可能であり、ライブラリモジュールを使用またはインストールする必要はありません。
その後、スクリプトはqr
参照演算子を使用して正規表現をプリコンパイルし、パフォーマンスを向上させます。各ループで同じ正規表現を再コンパイルすると、CPU時間が大幅に無駄になります。\b
部分一致を防止し、一致をキャッチするのを防ぐために使用される単語境界マーカー(一致項目が?:
発生したことを検出するだけで、一致を使用する必要がないため、これは時間の無駄です)。参照perldoc -f qr
とman perlre
正規表現は大文字と小文字を区別しますが、正規表現i
修飾子を使用して大文字と小文字を区別しないようにすることができます。
$re = qr/\b(?:$re)\b/i;
その後、スクリプトは残りの入力を読み取り、各入力行について、その行が正規表現と一致する場合は「true」を印刷し、そうでない場合は「false」を印刷します。これを使用するため、while(<>)
標準入力および/またはファイル名引数からデータを読み取ります。この例では、から入力を読み取りますfile1
。
メモリ使用量は、最初のファイルのサイズに比例します。単語が多ければ多いfile2
ほどRAMを使います。もちろん、実行時間は、最初のファイルのサイズと他のすべての入力に比例します。
答え4
tmp=$(mktemp)
comm -2 <(sort file1) <(sort file2) \
| sed -e 's/^\t.*/true/;t
c false' > "$tmp"
paste <(cat -n < "$tmp") \
<(cat -n file1 | sort -bk2) \
| sort -bk3,3n | cut -f2
出力:-
true
false
true
true
メモ:
- 最初のステップで2つのファイルをソートし、commを介して実行し、2番目のファイルを抑制します。
- 次のsedはtrue / false要素を識別します。
- 最後に、元の順序を復元するために、出力と数字でソートされた入力を貼り付けます。