列でテキストファイルを検索

列でテキストファイルを検索

次の形式のファイルがあります。

[#]   OWNER_NAME     NAME                       SIZE
[6]   Robottinosino  Software                   200
[42]  Robottinosino  Ideas worth zero           188
[12]  Robottinosino  Ideas worth zero or more   111
[13]  I am Batman    Hardware                   180
[25]  Robottinosino  Profile Pictures           170

コマンドラインツールを使用して次のことをしたいと思います。

my_command "Ideas worth zero"

結果は次のとおりです。

42

そして、次のような結果を得る危険を冒さないでください。

12

grepを使用して行を識別し、awkを使用して最初のフィールドを取得することを検討しましたが、テキスト「OWNER_NAME」と「SIZE」が表示される列を計算するのではなく、「NAME」フィールド全体を確実かつ効率的に一致させる方法よくわかりません。ヘッダーにスペースを切り取り、その間のすべての内容を取得します。

「OWNER_NAME」は 2 語以上にすることができます。たとえば、「OWNER_NAME」=「I am Batman」です。

どのアイデアとそれに応じた実装がありますか?

ここで使用するのは、cat、head、tail、awk、sed、grep、cutなどの古い系列だけです。

答え1

さて、列の長さがわからない場合は、bashよりも強力な言語に切り替えます。

#!/usr/bin/perl
use warnings;
use strict;

my $string = shift;
open my $FH, '<', '1.txt' or die $!;
my $first_line = <$FH>;
my ($before, $name) = $first_line =~ /(.* )(NAME *)/;
my $column = length $before;
$string .= ' ' x (length($name) - length $string);     # adjust the length of $string
while (<$FH>) {
    if ($column == index $_, $string, $column) {
        /^\[([0-9]+)\]/ and print "$1\n";
    }
}

答え2

フィールド幅が一定の場合(つまり、表示中のファイル形式が最大フィールド幅である場合)、GNU awk(gawk(1))を使用し、FIELDWIDTHS固定幅解析を使用するように変数を設定できます。

gawk -v searchstr="Ideas worth zero" -- '
    BEGIN { FIELDWIDTHS="6 15 27 5" }  # assuming the final field width is 5
    # Pre-process data
    {
        gsub(/[^[:digit:]]/, "", $1)  # strip out non-numbers
        for (i = 2; i <= NF; i++)
            gsub(/[[:space:]]*$/, "", $i)  # strip trailing whitespace
    }
    # match here
    $3 == searchstr { print $1 }
' file.txt

これをシェルスクリプトまたは関数でラップしてパラメータ化できますsearchstr-v searchstr="$1")。

ただし、フィールドの幅が可変の場合(たとえば、データが変更されるとフィールドの幅が変わる可能性がある)、最初の行をより賢明にチェックしてフィールドの幅を動的に決定する必要があります。OWNER_NAMEフィールドがアンダースコアを使用して呼び出された場合、フィールド名にスペースがないと想定されるため、スペースがフィールド名を区切ると想定できます。

BEGIN...定義したら、その行を次のコードに置き換えることができます。

NR == 1 {
    for (i = 2; i <= NF; i++)
        FIELDWIDTHS=FIELDWIDTHS index($0" ", " "$i" ")-index($0" ", " "$(i-1)" ") " "
    FIELDWIDTHS=FIELDWIDTHS "5"  # assuming 5 is the width of the last field
    next
}

次に、最初の行のフィールドを見て、2番目のフィールドから最後のフィールドまでの後続のフィールド位置間の差を計算してフィールドの幅を計算します。最後のフィールドの幅は5であると仮定しますが、そこに大きな数字を入力すると、残りのコンテンツとうまく機能するようです。

名前の前後にスペースを見つけてNAME内部フィールドが見つからないかOWNER_NAME(または名前付きフィールドがあるかどうかOWNER)、代わりにフィールド全体と一致することを確認する必要があります(また、一致するかどうかを確認$0するにはスペースを追加する必要があります)、最後にスペースがない場合でも一致)。

単に一致するのではなく、フィールド名で照会することを好むかもしれませんが$3、そのかどうかはあなたに任せます。

答え3

おそらく最も簡単な方法は、最初に「アイデア値0に基づいて行をフィルタリングし、次に行を捨てること」...以上」で行をフィルタリングすることです。

grep 'Ideas worth zero' | grep -v 'Ideas worth zero or more'

そのパイプから番号を取得するには、次のように入力します。

cut -d' ' -f1 | tr -d ']['

これにより、最初のフィールド(スペースで区切り)が切り捨てられ、角かっこが削除されます。

最善の方法は、適切なフィールド区切り文字があるようにファイル形式を少し変更できることです。

答え4

これは次のように役立ちます。

function my_command () {
    sed -n $(cut -b22-48 1.txt |
        grep -n "$1"' *$' |
        cut -f1 -d: )p 1.txt \
            | cut -d' ' -f1 | tr -d ']['
}

入力から関連列を切り取り、文字列が表示される行番号を検索し、その行を取得して最初の列の数だけを保持します。

関連情報