

bashを使って、１列目が同じ行の２列目を1つの行にまとめたいときどうすればよいですか？  
図で描くと

[code]

    a 1
    a 2
    a 3
    b 4
    c 5
    c 6
    d 7
    d 8
    e 9
    
[/code]

というテキストを

[code]

    a 1 2 3
    b 4
    c 5 6
    d 7 8
    e 9
    
[/code]

というふうに処理をしたいのです。お願いします。

追記  
テキストはすでにソートされたものです。結果はソートされていても、されていなくてもどちらで構いません。  
使っているbashのバージョンはversion 3.2.57です。

皆様の回答やコメントを受けて追記させていただきました。


CC BY-SA 3.0
2018-03-09T03:04:59.927
42249
2018-03-09T07:45:03.087
2018-03-09T07:45:03.087
3054
27669
question
bash shellscript
bashでのテキスト処理で１列目が同じ行の２列目を1つの行にまとめたい


質問通りbashで実装するのであれば対象のテキストが`a.txt`に格納されているとして

[code]

    unset -v HASH
    declare -A HASH
    while read k v; do HASH[$k]="${HASH[$k]} $v"; done < a.txt
    for k in ${!HASH[*]}; do echo $k ${HASH[$k]}; done
    
[/code]

でしょうか。Perl等、他のツールを使うのであればまた違ってくるとは思いますが。


CC BY-SA 3.0
2018-03-09T03:26:23.253
42251
2018-03-09T03:26:23.253
4236
42249
answer


入力が Field1 でソートされているとして、POSIX 互換 sh でごりおしするとこんな感じです。

[code]

    input() {
       cat <<EOF
    a 1
    a 2
    a 3
    b 4
    c 5
    c 6
    d 7
    d 8
    e 9
    EOF
    }
    
    efficient_sh() (
        prev=
        while read -r k v
        do
            if [ "$prev" = "" ]; then
                printf '%s %s' $k "$v"
                prev=$k
            elif [ "$prev" = $k ]; then
                printf ' %s' "$v"
            else
                printf '\n%s %s' $k "$v"
                prev=$k
            fi
        done
        echo
    )
    
    input | efficient_sh
    # a 1 2 3
    # b 4
    # c 5 6
    # d 7 8
    # e 9
    
[/code]


CC BY-SA 3.0
2018-03-09T05:48:50.700
42254
2018-03-09T05:48:50.700
754
42249
answer


[Yuki Inoue](https://ja.stackoverflow.com/users/754/yuki-inoue)さんの回答の亜種です。  
ソート済みのデータが同ディレクトリにdata.txtとして保存されていることを前提としています。

[code]

    #!/bin/sh
    set -Cu
    #set -vx
    
    while read -r key val; do
        if test "${current:-}" != "$key"; then
            current="$key"
            printf "\n%s" "$key"
        fi
        printf " %s" "$val"
    done <data.txt
    echo
    
    exit 0
    
[/code]


CC BY-SA 3.0
2018-03-09T06:50:06.317
42258
2018-03-09T07:11:39.957
2018-03-09T07:11:39.957
25936
25936
42249
answer


外部コマンドを使う例です。Bashの拡張機能は使用していません。

### awkの例

Awkは標準的なコマンドです。基本的にインストールは不要です。

[code]

    #!/bin/sh
    awk '
        BEGIN {
            ORS = ""
            pre = ""
        }
        {
            if ($1 != pre) {
                if (pre != "") {
                    print "\n"
                }
                print $1 " " $2
                pre = $1
            } else {
                print " " $2
            }
        }
        END { print "\n" }
    ' in.txt
    
[/code]

### datamashの例

[code]

    #!/bin/sh
    datamash --field-separator=' ' --group=1 collapse 2 <in.txt \
        |tr , ' '
    
[/code]

[GNU
datamash](https://www.gnu.org/software/datamash/)は標準的なコマンドではありません。多くの場合、インストールが必要です。  
上の例では、データがカンマ（`,`）を含まない事を前提としています。


CC BY-SA 3.0
2018-03-09T07:40:30.933
42263
2018-03-09T07:40:30.933
3054
42249
answer