2013-01-25 8 views
11

の最後の行を除いて、カンマ。私は実際に複数の列の値を1つの行に書き込んでいます。しかし、私が最後の行にいない場合にのみ、カンマを印刷したいと思います。

答えて

5

私は、スクリプトを実行する前に行数を見つけることでそれを行います。 coreutilsとbash:

awk -v nlines=$(wc -l < $a) '{printf "%s", $1"-"$2} NR != nlines { printf ", " }' $a >>positions 

ファイルに2列しかない場合は、次のようなcoreutilsの代替も有効です。例えばデータ:

paste <(seq 5) <(seq 5 -1 1) | tee testfile 

出力:

1 5 
2 4 
3 3 
4 2 
5 1 

すぐ改行とタブを置き換え、pasteは容易に所望の形式に日付を組み立てる:

<testfile tr '\t' '\n' | paste -sd-, 

出力:

1-5,2-4,3-3,4-2,5-1 
+0

ありがとうございました! – Perlnika

+0

FYI、この 'tr'と' paste'の使用法は、POSIX環境で動作するはずです。GNU coreutilsに限られていません。 – ghoti

1

ここでのcoreutilsに頼ることなく、より良い方法です:

awk 'FNR==NR { c++; next } { ORS = (FNR==c ? "\n" : ", "); print $1, $2 }' OFS="-" file file 
+0

これはThorのものよりもなぜ優れていますか?あなたはいつawkを持っていますが、wcは持っていませんか? –

+0

@ MichaelJ.Barber:壊れたインストールは簡単な答えです。それにもかかわらず、最初のパスに 'wc -l <​​file'をエミュレートするコードを書くだけで、2回目のパスで必要に応じて' ORS'を操作するのははるかに面倒です。簡単です。また、あなたの答えは不完全です。 'END'ブロックにもう1つのprintステートメントが必要です。これは、EOFに改行文字がないことを修正するためです。それは本当に何もしない3つのプリントステートメントです。大きなファイルでは、あなたのアプローチは遅くなります。 Sarathiの答えは、各行がメモリに追加され、それが理想的ではないため、非常に大きなファイルでは遅くなります。 – Steve

+1

また、本当に1つだけが必要な場合、Thorは2つのprintステートメントを使用しています。彼は、必要なものが書き直されたときに迅速な修正を加えました。 HTH。 – Steve

19

シングルパス・アプローチ:私は、文字列の書式設定を簡素化しました

cat "$a" | # look, I can use this in a pipeline! 
    awk 'NR > 1 { printf(", ") } { printf("%s-%s", $1, $2) }' 

注意。

+1

ちょっと見て、あなたは 'awk <" $ a "'Nr ...''を使うこともでき、それは1つのプロセス(猫なし)で動くでしょう! –

+1

@KrzysztofJabłoński「cat」の使用に対する熱狂的な憎しみは、ここでは厳然としていますが、あなたの選択肢はそのポイントを完全に逃しています。他の答えは、ここで示したパイプラインでの使用を許可しなかったマルチパスの手法に依存していました。 –

+0

パイプラインでの使用状況を表示することが目的であることに同意します。明らかに、私はその目的のために 'cat'の使用に同意しません。あまりにもしばしば私は、その謙虚な命令がパイプラインを始めることに慣れているのを見ます。このアイデアを説明するために、私は 'dataGeneratorCmd | dataFilterCmd | awk '...' | terminalConsumerCmd'。しかし、それは好みと好みの問題です。とにかく、怒らないでください。これはまだかなり包括的な答えです。 +1 –

0
awk '{a[NR]=$1"-"$2;next}END{for(i=1;i<NR;i++){print a[i]", " }}' $a > positions 
9

、このいずれかをお楽しみください:

awk '{printf t $1"-"$2} {t=", "}' $a >> positions 

葉、一見少しトリッキーに見えます。だから私は、説明のは、明確にするためprintprintfを変更でき、すべての最初ます:

awk '{print t $1"-"$2} {t=", "}' file 

、それが何を見て、例えば、この単純なコンテンツを持つファイルのために:

1 A 
2 B 
3 C 
4 D 

ので、それは次のように生成されます。

1-A 
, 2-B 
, 3-C 
, 4-D 

トリックは、初めに空である、先行するt変数です。変数はと表示された後の処理の次のステップでのみ{t=...}に設定されます。したがって、我々が(awk)反復を続けるなら、我々は所望のシーケンスを得るでしょう。

+0

あなたの回答は低品質レビューキューに表示されました。あなたの答えを編集して、この仕組みの説明を含めてください。 –

+0

この解決法は、awkの前に行数が計算できない、または計算が難しいため、受け入れられた答えよりも優れています。たとえば、空行に遭遇した場合にwakeを終了させる場合は、 – leftjoin

+0

これはいいですが、ヘッダー行をスキップする条件に基づいて行を印刷すると、最初の行をスキップした後で動作しないので、 't' gots割り当てられた。 '{t ="、 "}"と同じ条件を使うことで動作しますが、私にとっては少し汚れているようです。例: '$ 1〜/^[0-9] * $/{print t $ 1} $ 1〜/^[0-9] * $/{t ="、 "}'クリーナーソリューションはありませんか? – r1verside

1

あなたはAWKのORSとOFSは、これを処理する合理的な方法だろうと思うかもしれない:

$ awk '{print $1,$2}' OFS="-" ORS=", " input.txt 

しかし、入力が最後の行に改行が含まれているため、これは、最終的なORSになります。改行はレコードセパレータなので、awkの観点からは入力に空の最後のレコードがあります。これを回避するには、ちょっとした騒ぎで作業することができますが、結果として複雑さが増すと、1ライナーの優雅さが失われます。

ここにこれを取ります。あなたが「複数の列の値を書いている」と言っているので、ORSとOFSを使っていないと問題が起こる可能性があります。したがって、フォーマットを使用して完全に目的の出力を得ることができます。

$ cat input.txt 
3 2 
5 4 
1 8 
$ awk '{printf "%s%d-%d",t,$1,$2; t=", "} END{print ""}' input.txt 
3-2, 5-4, 1-8 

これはマイケルのとルークのシングルパスのアプローチに似ていますが、それは、単一のprintfを使用して、正しくフォーマットするためのフォーマット文字列を使用しています。

これはマイケルのソリューションよりも無視できるほど優れていると思われます。なぜなら、ファイルの読み込みが一度だけで済むため、割り当てはテストよりもCPU消費が少なく、マルチパスソリューションよりもはるかに優れているからです。

関連する問題

 関連する問題