grepが改行なしで行を無視するようにする方法

grepが改行なしで行を無視するようにする方法

ファイル内の文字列を検索したいが末尾の改行で終わらない行の一致を無視します。つまり、ファイルの最後の行が改行で終わらない場合は無視したいと思います。

これを行う最良の方法は何ですか?

subprocess処理する前に、大きなテキストログファイルをフィルタリングするためにモジュールを介してgrepを呼び出すPythonスクリプトでこの問題が発生しました。ファイルの最後の行が記録されている可能性があり、その場合はその行を処理したくありません。

答え1

使用gawk(EREのようなものを使用grep -E):

gawk '/pattern/ && RT' file

RTin には、gawkレコード区切り文字に一致するコンテンツが含まれます。RSデフォルト値RS\n)を使用すると、最後に\n区切られていないレコードを除いて、レコードRTは空です。

使用perl(使用可能なものと同様のPerl RE grep -P):

perl -ne 'print if /pattern/ && /\n\z/'

gawkgrepまたはとは異なり、perlデフォルトは文字ではなくバイトに適用されます。たとえば、.正規表現演算子は、UTF-8でエンコードされた2バイトのそれぞれと一致します£。ロケールの文字定義(例:awk/)に従って文字を処理するには、次のようにgrepします。

perl -Mopen=locale -ne 'print if /pattern/ && /\n\z/'

答え2

grep明らかに限られた改行文字は無視されるため、実際には使用できません。sed現在行(断片)が改行で終わるかどうかを内部的に知っていますが、その情報を公開するように強制する方法はわかりません。awk改行文字(RS)でレコードを区別しますが、改行文字があるかどうかは実際には関係ありません。デフォルトの動作は、すべての場合、最後に改行文字()を印刷することですprintORS

したがって、一般的なツールはここではあまり役に立たないようです。

しかし、sed最後の行で作業する時期を知っているので、最後の行の一部を見ずに最後の行全体を失っても構わない場合は、最後の行と思われるものをsed削除できます。例えば

sed -n -e '$d' -e '/pattern/p'  < somefile                   # or
< somefile sed '$d' | grep ...

それがオプションでない場合は、常にPerlがあります。これにより、/pattern/末尾に改行文字を含む一致する行のみが印刷されます。

perl -ne 'print if /pattern/ && /\n$/'

答え3

次の操作が実行されます。

#!/usr/bin/env sh

if [ "$(tail -c 1 FILE)" = "" ]
then
    printf "Trailing newline found\n"
    # grep whole file
    # grep ....
else
    printf "No trailing newline found\n"
    # ignore last line
    # head -n -1 FILE | grep ...
fi

以下に説明するコマンド置換機能を使用しますman bash

Bash はコマンドを実行し、コマンド置換をコマンドの標準出力に置き換えて拡張を実行します。何でも 末尾の改行を削除します。

答え4

速度が必要な場合は、CでPCRE(または他のより速い正規表現ライブラリ)を使用すると、正規表現を使用して改行を確認できます。欠点:新しいコードを維持してデバッグする必要があり、部分を再実装するのにかかる時間は、式の複雑さやそのような機能を使用するかどうかによって異なりますgrepperl--only-matching

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <pcre.h>
#define MAX_OFFSET 3

int main(int argc, char *argv[])
{
    // getline
    char *line = NULL;
    size_t linebuflen = 0;
    ssize_t numchars;
    // PCRE
    const char *error;
    int erroffset, rc;
    int offsets[MAX_OFFSET];
    pcre *re;

    if (argc < 2) errx(1, "need regex");
    argv++;
    if ((re = pcre_compile(*argv, 0, &error, &erroffset, NULL)) == NULL)
        err(1, "pcre_compile failed at offset %d: %s", erroffset, error);

    while ((numchars = getline(&line, &linebuflen, stdin)) > 0) {
        if (line[numchars-1] != '\n') break;
        rc = pcre_exec(re, NULL, line, numchars, 0, 0, offsets, MAX_OFFSET);
        if (rc > 0) fwrite(line, numchars, 1, stdout);
    }
    exit(EXIT_SUCCESS);
}

これはperl -ne 'print if /.../ && /\n\z/'

関連情報