
2つのファイルがあり、それぞれ約100,000のスペースで区切られた列があります。両方のファイルの各列を抽出し、別々のファイルに書き込み、コマンド2を実行したいと思います。
2つの列を持つファイルの例。
cat test1.txt
rr1 rr2
1 2
1 2
1 1
2 1
cat test2.txt
rr1 rr2
2 2
1 1
2 1
2 2
test1.txtの最初の列とtest2.txtの最初の列を取得し、これら2つの列が並んでいる新しいファイルという新しいファイルを作成したいと思います。これまで試したコードは次のとおりです。
awk -F' ' '{
for(i=1; i<=NF; i++){ # iterate over each column
paste -d' ' <(sed 1d test1.txt | awk -v var1="$i" '{print $var1}') <(sed 1d test2.txt | awk -v var2="$i" '{print $var2}' ) > out$i
# write to file named with column name or i
# do command2 for out$1
}
}'
ただし、このコードはエラーを発生させます。
awk: cmd. line:3: paste -d
awk: cmd. line:3: ^ unexpected newline or end of string
期待される出力
cat out1
1 2
1 1
1 2
2 2
cat out2
2 2
2 1
1 1
1 2
私はこれに多くの時間を費やしましたが、この問題を解決する方法がわかりません。どんな助けでも大変感謝します。もっと良い方法がありますか?
答え1
このようにしてください強く打つそしてアッツールボックスから列数と一般的なコマンドを取得します。
#!/bin/bash
for i in $(seq 1 $(awk '{print NF;exit}' test1.txt)); do
paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
<(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done
または
#!/bin/bash
numcols=$(awk '{print NF;exit}' test1.txt)
for ((i=1; i<=numcols; i++)); do
paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
<(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done
または使用ケシ:
#!/bin/ksh
numcols=$(awk '{print NF;exit}' test1.txt)
for i in {1..$numcols}; do
paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
<(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done
それから:
cat out.1
cat out.2
コメントに記載されているように、あなたのようにawk
混在させることはできませんshell
。
開発者でない場合は、ここで行ったように基本的なシェルコマンドを習得するのが最善です。
次の基本コマンドのドキュメントをお読みください。
tr
paste
sed
seq
それは基本的ではありません(ここでは簡単な方法で使用されます)。
awk
一時ファイル名で置換>(command ...)
または置換を処理します。<(...)
このファイルを書き込んだり読み込んだりすると、バイトは内部コマンドにパイプされます。通常はファイルリダイレクトと一緒に使用されますcmd1 2> >(cmd2)
。
望むより:
http://mywiki.wooledge.org/ProcessSubstitution
http://mywiki.wooledge.org/BashFAQ/024
答え2
仮定:
- すべてのファイルには少なくとも1行(タイトル)があります。
- すべてのファイルの行数は同じです。
- すべてのファイルの列数は同じです。
awk
すべてのファイルは(配列を介して)メモリに保存できます。
一般的なアプローチ:
- 多次元配列を使用
GNU awk
できますが、副作用は(単一次元インデックスよりも)より多くのメモリを使用することです。 column # (NF)
row number (FNR)
インデックス++を使用してデータを1次元配列に格納します。file count
END{...}
ブロックでは、データをout{1..NF}
ファイルとして印刷する配列を繰り返します。
以下のみを使用してくださいawk
。
$ cat merge.awk
FNR==1 { fcnt++ } # keep track of number of files
FNR>1 { for (i=1; i<=NF; i++) # loop through columns
lines[i,FNR,fcnt]=$i # index = column # + row number + file count
}
END { for (i=1; i<=NF; i++) { # loop through columns
for (j=2; j<=FNR; j++) # loop through rows
for (k=1; k<=fcnt; k++) # loop through filecount
printf "%s%s", lines[i,j,k], (k<fcnt ? OFS : ORS), lines[i,j,k] > ("out" i)
close ("out" i)
}
}
OPの2つのファイルに対して実行します。
$ awk -f merge.awk test1.txt test2.txt
$ head out?
==> out1 <==
1 2
1 1
1 2
2 2
==> out2 <==
2 2
2 1
1 1
1 2
3つの新しいファイル:
$ head t?.txt
==> t1.txt <==
rr1 rr2 rr3
1 2 3
4 5 6
7 8 9
==> t2.txt <==
rr1 rr2 rr3
a b c
d e f
g h i
==> t3.txt <==
rr1 rr2 rr3
X XX XXX
Y YY YYY
Z ZZ ZZZ
次の3つのファイルに対して実行します。
$ awk -f merge.awk t1.txt t2.txt t3.txt
$ head out?
==> out1 <==
1 a X
4 d Y
7 g Z
==> out2 <==
2 b XX
5 e YY
8 h ZZ
==> out3 <==
3 c XXX
6 f YYY
9 i ZZZ
答え3
このエラーは、一重引用符で囲まれた文字列内に一重引用符を使用できないことが原因で発生します。このawk
コマンドは、プログラムをpaste -d
プログラムawk
(切り捨てによる構文エラーを含む)として処理し、残りのコード(引用符なしの空白まで)を処理する最初のファイル名などで処理します。また、シェルプログラム内ではコマンドを使用できませんawk
。
awk
次のパイプラインは、使用されたコマンドに2つのファイルを並べて供給しますpaste
。このawk
コマンドは、各ファイルの列ペアを各列の別の出力ファイルに出力します。
$ paste test1.txt test2.txt | awk 'NR > 1 { for (i = 1; i <= NF/2; ++i) print $i, $(NF/2+i) >("out" i) }'
$ cat out1
1 2
1 1
1 2
2 2
$ cat out2
2 2
2 1
1 1
1 2
awk
美しく印刷されたコード:
NR > 1 {
for (i = 1; i <= NF/2; ++i)
print $i, $(NF/2+i) > ("out" i)
}
NF/2
最初の行に入力されたヘッダーを無視し、このコードはファイルの1つのフィールドを繰り返します(両方のファイルが同じ数のフィールドを持ち、2つのファイルのフィールドが同じ順序でペアでなければならないと仮定します)。フィールド、つまり私たちにフィールドの半分が与えられます。次に、フィールド番号の後に名前の付いたファイルにその番号を追加して、i
その番号と一緒に最初のフィールドを印刷します。NF/2
out
i
わずかに変更するだけで、最初のファイルのヘッダーに基づいて出力ファイルの名前を指定できます(2番目のファイルのヘッダーは無視され、同じ順序であると仮定します)。
NR == 1 {
for (i = 1; i <= NF/2; ++i) head[i] = $i
next
}
{
for (i = 1; i <= NF/2; ++i)
print $i, $(NF/2+i) > head[i]
}
質問に提供されたデータに基づいて両方のファイルが生成されrr1
(rr2
またはすでに存在する場合は上書きされます)。
以下のコメントで正しく指摘されているように(現在のコメントは削除されています)、上記の内容は100000列の「開いたファイルが多すぎます」エラーを引き起こす可能性があり、そのawk
実装は開かれたファイル記述子プールをインテリジェントに管理しません(GNUのようにawk
)。 。他のawk
実装では、毎回出力ファイルを閉じますprint
。>>
>
awk
上記の最後のクリップを修正したバージョンは次のとおりです。
NR == 1 {
for (i = 1; i <= NF/2; ++i) head[i] = $i
next
}
{
for (i = 1; i <= NF/2; ++i) {
print $i, $(NF/2+i) >> head[i]
close(head[i])
}
}
答え4
col_co=$(awk 'END{print NF}' f1.txt)
for ((i=1;i<=$col_co;i++))
do
awk -v i="$i" 'NR>1{print $i}' f1.txt|paste >file_1.txt
awk -v i="$i" 'NR>1{print $i}' f2.txt >file_2.txt
paste file_1.txt file_2.txt >out_new_$i.txt
done
出力
cat out_new_1.txt
1 2
1 1
1 2
2 2
cat out_new_2.txt
2 2
2 1
1 1
1 2