列によるテキストファイルの検索

列によるテキストファイルの検索

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

[#]   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' は複数の単語になる可能性があることに注意してください。例: '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")。

ただし、フィールドの幅が可変である場合、つまり、データが変更されるとフィールドの幅が変わる可能性がある場合は、もう少し工夫して、最初の行を調べてフィールドの幅を動的に決定する必要があります。 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

おそらく最も単純なのは、最初に「価値ゼロのアイデア」で行をフィルタリングし、次に「...以上」の行を捨てることです。

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 ']['
}

入力から関連する列のみを切り取り、文字列が出現する行番号を検索し、この行を取得して、その行の最初の列の番号のみを保持します。

関連情報