親と子の関係を達成するためにデータを反転する必要があるシナリオがあります。私のソースデータは次のとおりです。
Key_Col |階層
1|a、b、c、d
2|a、b、c、d、e
私の予想結果は次のとおりです。
主な学校 | 子供 | 親 |
---|---|---|
1 | D | 氏 |
1 | 氏 | 第二 |
1 | 第二 | ㅏ |
1 | ㅏ | 無効 |
2 | 金利 | D |
2 | D | 氏 |
2 | 氏 | 第二 |
2 | 第二 | ㅏ |
2 | ㅏ | 無効 |
bashスクリプトでこれを達成する方法を教えてください。
私が使用するスクリプトは次のとおりです。
Var="1|a,b,c,d";
for i in $Var
do
Key=`echo $i |cut -d'|' -f1`
Hierarchy=`echo $i |cut -d'|' -f2`
Delim_Count=`echo ${Hierarchy} |awk -F',' '{ print NF-1 }'`
for (( c=$Delim_Count+1; c>=1; c-- ))
do
Parent=`echo ${Hierarchy} |cut -d',' -f$c`
Prev=`expr $c - 1`
if [ $Prev -ne 0 ]; then
Child=`echo ${Hierarchy} |cut -d',' -f${Prev}`
echo "${Key}|${Parent}|${Child}"
else
echo "${Key}|${Parent}|"
fi
done
done
ところが問題は100行を超えるとスクリプトを完成するのに長い時間がかかるという点だ。
ベストアンサー1
この種の作業は、通常、テキストまたは構造化データを処理するように設計された言語を使用する方が簡単です。標準テキスト処理ユーティリティを使用したソリューションawk
と使用法は次のとおりです。ミラー(mlr
)は構造化データ(データはCSV形式)操作用に特別に設計されたツールです。
そしてawk
:
$ cat file
Key_Col|Hierarchy
1|a,b,c,d
2|a,b,c,d,e
$ awk 'BEGIN { OFS=FS="|" } NR == 1 { print $1, "Child", "Parent"; next } { n=split($2,a,","); a[0]="null"; for (i=n;i>0;i--) print $1,a[i],a[i-1] }' file
Key_Col|Child|Parent
1|d|c
1|c|b
1|b|a
1|a|null
2|e|d
2|d|c
2|c|b
2|b|a
2|a|null
上記のコードは、各入力行を区切られた一連のawk
フィールドに読み込みます。|
コンマの2番目のフィールドを配列に分割しますa
。配列の0番目の要素は文字列null
(split()
最初のインデックス1を使用した配列の作成したがって、データを上書きせずにインデックス0を使用できることがわかります。次に、配列の終わりから始まりまで繰り返し、最初のフィールドの値と現在の配列要素と配列の前の要素を出力します。最後の反復に達すると、ループ変数の値は1になり、a[1]
(a[0]
)が印刷されます。null
タイトルを含む入力の最初の行は異なる方法で処理されます。分割などを行う代わりに、コードは入力の最初のフィールドとChild
文字列とParent
。条件付きNR==1
ブロックはこれを行います。
読みやすくするためにコード形式が変更されましたawk
。
BEGIN {
OFS = FS = "|"
}
NR == 1 {
print $1, "Child", "Parent"
next
}
{
n = split($2, a, ",")
a[0] = "null"
for (i = n; i > 0; i--)
print $1, a[i], a[i-1]
}
入力はCSVのように見えるので、CSV認識ツールを使用して処理する方が安全です。 Miller( mlr
) は次のツールです。
$ mlr --csv --fs pipe put -q 'm=splitnv($Hierarchy,","); m[0]="null"; for (var i=length(m)-1;i>0;i-=1) { emit {"Key_Col": $Key_Col, "Child": m[i], "Parent": m[i-1] } }' file
Key_Col|Child|Parent
1|d|c
1|c|b
1|b|a
1|a|null
2|e|d
2|d|c
2|c|b
2|b|a
2|a|null
Millerput
式は上記のコードと同じ概要に従いますが、awk
Millerはこれらのヘッダーを読み取って使用する方法を知っているため、ヘッダーを特別なケースとして扱う必要はありません。
m = splitnv($Hierarchy, ",")
m[0] = "null"
for (var i = length(m) - 1; i > 0; i -= 1) {
emit {
"Key_Col": $Key_Col,
"Child": m[i],
"Parent": m[i-1]
}
}
put
Millerを使用すると、サブコマンドの前のオプションを調整してさまざまな形式の結果を生成できます。
きれいに印刷された「禁止された」出力:
$ mlr --c2p --barred --ifs pipe put ...as above...
+---------+-------+--------+
| Key_Col | Child | Parent |
+---------+-------+--------+
| 1 | d | c |
| 1 | c | b |
| 1 | b | a |
| 1 | a | null |
| 2 | e | d |
| 2 | d | c |
| 2 | c | b |
| 2 | b | a |
| 2 | a | null |
+---------+-------+--------+
JSON:
$ mlr --c2j --ifs pipe put ...as above...
{ "Key_Col": 1, "Child": "d", "Parent": "c" }
{ "Key_Col": 1, "Child": "c", "Parent": "b" }
{ "Key_Col": 1, "Child": "b", "Parent": "a" }
{ "Key_Col": 1, "Child": "a", "Parent": "null" }
{ "Key_Col": 2, "Child": "e", "Parent": "d" }
{ "Key_Col": 2, "Child": "d", "Parent": "c" }
{ "Key_Col": 2, "Child": "c", "Parent": "b" }
{ "Key_Col": 2, "Child": "b", "Parent": "a" }
{ "Key_Col": 2, "Child": "a", "Parent": "null" }
(等)