[解決済み] Bashでファイルを転置する効率的な方法
質問
次のような形式の巨大なタブ区切りファイルを持っています。
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
私は 転置 をbashコマンドだけで効率的に行いたい(これを行うために10数行のPerlスクリプトを書くこともできるが、ネイティブのbash関数よりも実行速度が遅くなるはずである)。そのため、出力は次のようになります。
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
こんな解決策を考えてみました。
cols=`head -n 1 input | wc -w`
for (( i=1; i <= $cols; i++))
do cut -f $i input | tr $'\n' $'\t' | sed -e "s/\t$/\n/g" >> output
done
しかし、それは遅いし、最も効率的なソリューションとは思えません。私は、viのための解決策を このポスト でviの解決策を見ましたが、やはり遅すぎです。どんな考え/提案/素晴らしいアイデアでも?:-)
どのように解決するのですか?
awk '
{
for (i=1; i<=NF; i++) {
a[NR,i] = $i
}
}
NF>p { p = NF }
END {
for(j=1; j<=p; j++) {
str=a[1,j]
for(i=2; i<=NR; i++){
str=str" "a[i,j];
}
print str
}
}' file
出力
$ more file
0 1 2
3 4 5
6 7 8
9 10 11
$ ./shell.sh
0 3 6 9
1 4 7 10
2 5 8 11
Jonathanによる10000行のファイルでのPerlソリューションに対するパフォーマンス
$ head -5 file
1 0 1 2
2 3 4 5
3 6 7 8
4 9 10 11
1 0 1 2
$ wc -l < file
10000
$ time perl test.pl file >/dev/null
real 0m0.480s
user 0m0.442s
sys 0m0.026s
$ time awk -f test.awk file >/dev/null
real 0m0.382s
user 0m0.367s
sys 0m0.011s
$ time perl test.pl file >/dev/null
real 0m0.481s
user 0m0.431s
sys 0m0.022s
$ time awk -f test.awk file >/dev/null
real 0m0.390s
user 0m0.370s
sys 0m0.010s
Ed Morton氏による編集(@ghostdog74は不服なら自由に削除してください)。
このバージョンでは、変数名をより明確にすることで、以下の質問に答え、スクリプトが何を行っているかを一般的に明確にすることができます。また、OP がもともと求めていた区切り文字としてタブを使用し、空のフィールドを処理できるようにし、偶然にもこの特定のケースで出力を少しきれいにしています。
$ cat tst.awk
BEGIN { FS=OFS="\t" }
{
for (rowNr=1;rowNr<=NF;rowNr++) {
cell[rowNr,NR] = $rowNr
}
maxRows = (NF > maxRows ? NF : maxRows)
maxCols = NR
}
END {
for (rowNr=1;rowNr<=maxRows;rowNr++) {
for (colNr=1;colNr<=maxCols;colNr++) {
printf "%s%s", cell[rowNr,colNr], (colNr < maxCols ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
上記の解決策はどのような awk でも動作します (もちろん、古くて壊れた awk は除きます - そこは YMMV)。
上記の解決策はファイル全体をメモリに読み込みますが、入力ファイルが大きすぎて読み込めない場合は、この方法をとります。
$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ printf "%s%s", (FNR>1 ? OFS : ""), $ARGIND }
ENDFILE {
print ""
if (ARGIND < NF) {
ARGV[ARGC] = FILENAME
ARGC++
}
}
$ awk -f tst.awk file
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
はほとんどメモリを使いませんが、入力ファイルを行のフィールド数だけ読み込むので、ファイル全体をメモリに読み込むバージョンよりずっと遅くなります。また、各行のフィールド数が同じであることを仮定しており、GNU awk を使って
ENDFILE
と
ARGIND
をテストすることができますが、どんな awk でも同じことができます。
FNR==1
と
END
.
関連
-
[解決済み] Bashスクリプトのソースディレクトリをスクリプト自体から取得するにはどうすればよいですか?
-
[解決済み] Bashシェルスクリプトでディレクトリが存在するかどうかを確認するにはどうすればよいですか?
-
[解決済み] Bashで通常のファイルが存在しないかどうかを判断する方法を教えてください。
-
[解決済み] Bashで文字列変数を連結する方法
-
[解決済み] Bashで文字列が部分文字列を含むかどうかをチェックする方法
-
[解決済み] Bash prints リテラルの改行をエコーする \n
-
[解決済み] Bashスクリプトからプログラムが存在するかどうかを確認するにはどうすればよいですか?
-
[解決済み] Bashでファイルの中身をループする
-
[解決済み】Bashでファイル名と拡張子を抽出する。
-
[解決済み] MSYSでフォルダを閲覧する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Bashでユーザの入力を変数に読み込むには?
-
[解決済み] bash に次の行に進むように指示する方法
-
[解決済み] シェルプログラミングで「export」は何をするのですか?重複
-
[解決済み] bashのタブ補完はどのように機能するのですか?
-
[解決済み] シェルスクリプトで文字列が空でもスペースでもないことをチェックする
-
[解決済み] スクリプトの実行に関連したファイルの参照
-
[解決済み] このbashのフォーク爆弾はどのように機能するのでしょうか?重複
-
[解決済み] set -u` による Bash による空の配列展開
-
[解決済み] unset の使用と変数への空の設定
-
[解決済み] MSYSでフォルダを閲覧する