awkまたはsedを介して最初の列に行を結合する

awkまたはsedを介して最初の列に行を結合する

awk次の状況ではどのように使用しますか?

同じ列で始まる行を連結したいと思います。結合後、最初の列(この場合)のみがaaa保持されwwwますhhh

ファイルはスペースまたはタブで区切ることができます。

入力例:

aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL

希望の出力:

aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL

背景は、最初の列が常にエンティティの識別子である非常に単純なファイルベースのデータベースを構築したいということです。同じ識別子列に基づくすべての行が連結されます。

答え1

awkを使用して各行の最初の列を取得するには、次のようにします。

< testfile awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh

これは残りの行のキーです。したがって、行の最初の列をキーとして使用し、行の2番目の列を値としてハッシュテーブルを作成できます。

< testfile awk '{table[$1]=table[$1] $2;} END {for (key in table) print key " => " table[key];}'
www => yyy
aaa => bbbNULLbbb
hhh => 111111

残りの行(2列目から始まる)を取得するには、すべての列を収集する必要があります。

< testfile awk '{line="";for (i = 2; i <= NF; i++) line = line $i " "; table[$1]=table[$1] line;} END {for (key in table) print key " => " table[key];}'
www => yyy hhh NULL NULL NULL NULL 
aaa => bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc    NULL NULL NULL NULL 
hhh => 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL 

答え2

他の人はawkまたはsedで答えたかもしれませんが、Pythonのバージョンはシンプルで役に立ちます。

#!/usr/bin/env python

input_file = 'input.dat'
in_fh      = open(input_file, 'r')

input_order = []
seen        = {}
for line in in_fh:    
    # Remove the newline character...
    line = line[:-1]

    # Separate the first column from the rest of the line...
    key_col, sep, rest_of_line = line.partition(" ")
    rest_of_line = sep + rest_of_line  

    # If we've seen this key already, concatenate the line...
    if key_col in seen:
        seen[key_col] += rest_of_line
    # ...otherwise, record the ordering, and store the new info
    else:
        input_order.append(key_col)
        seen[key_col] = rest_of_line

in_fh.close()

# Dump the ordered output to stdout
for unique_col in input_order:
    print unique_col + seen[unique_col]

答え3

これはcoreutilsの興味深いアプリケーションです。入力のすべての行に対して結合を呼び出すので、大きな入力ではそれほど効率的ではないと考えられます。

touch outfile
while read; do
  join -a1 -a2 outfile <(echo $REPLY) > tmp
  mv tmp outfile
done < infile

効率を上げるには、仮想ディスクoutfileに保存するのが役立ちます。tmp

編集する

または一時ファイルがありません。

out=""
while read; do
  out=$(join -a1 -a2 <(echo -n "$out") <(echo -n "$REPLY"))
done < infile

echo "$out"

答え4

OP仮定求める入力順序を維持希望の出力、関連(文字列インデックス)配列はここでは使用できません。そのキーに対して反復を使用すると、出力順序はfor (var in array)PerlのハッシュやPython辞書のように予測できません。3.6以前

以下は、入力ファイルに最初に表示されたキーの元の順序を維持する、よく説明されたバニラAWKソリューションです。重複排除残りコラムはまた別の挑戦だろうが、必ずしもそうする必要はないようです。

これは、BSDバージョンのAWKを使用してmacOSでテストされました。しなければならない持っているすべてを活用して作業してくださいawk。必要にchmod +x concat-by-first-col応じて、最初に実行可能ファイルとしてマークした場合は、スタンドアロンシェルスクリプト*(AWKで書かれたことを誰も知らない)として実行できます。

#!/usr/bin/awk -f
##  concat-by-first-col
##    concatenate values from lines beginning with the same first column
##
##  usage:
##    $ chmod +x concat-by-first-col
##    $ ./concat-by-first-col inputfile > outputfile

BEGIN {
    # default output separator *is* a space, but if you wanted to change it…
    OFS = " "
}

{
    # assuming EVERY input record has AT LEAST the key…
    # append to `keys` if we haven’t seen this key before
    if (!($1 in values))
        keys[length(keys)+1] = $1

    # append second and subsequent columns to what we already have
    for (i = 2; i <= NF; i++)
        # insert an OFS *only* if there’s an existing value for this key
        values[$1] = (values[$1] ? values[$1] OFS : "") $i
}

END {
    # for all the keys, in the order they appeared in the input…
    for (i = 1; i <= length(keys); i++) {
        key = keys[i]
        # a comma stands in for OFS in AWK’s `print` statement
        print key, values[key]
    }
}

スタンドアロンスクリプトの代わりにスイッチを使用して、AWKにプログラムテキストを提供できます-f

awk -f concat-by-first-col inputfile > outputfile

ブロックも省略した場合は、コマンドラインで指定BEGINできます。OFS

awk -v OFS='\t' -f concat-by-first-col inputfile > outputfile

希望の出力と一致しますか?確認する:

echo "aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL" > input

echo "aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL" > expected

# uses Bash’s process substitution**; expect no output
diff expected <(./concat-by-first-col input)

*のため理由、Linuxを使用している場合、またはAWKバージョンではない場合は、システムが/usr/bin/awk「shebang」行を調整する必要があるかもしれません。

**https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html

関連情報