最初の3列を除くすべてを印刷する


112

面倒すぎる:

awk '{print " "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13}' things

43
使用できない理由はありますcut -f3-か?
Cascabel

1
@hhh nice one ..私は要約回答のアイデアが好きです。
Chris Seymour

2
@Jefromi -持っていないawkのカット、とのラインバッファリングの問題があるので:stackoverflow.com/questions/14360640/...
sdaau


@Jefromi- アクションのcut前にも正規表現がなく{}、フィールド区切り文字(スペースの数は可変ですか?)があるので、手動で指定する必要があります。OP shift Nは存在しないコマンドについて聞きたかったのだと思います。最も近いのは$1="";$2="";(...);print}ですが、私の場合、いくつかの先行スペース(おそらくセパレータ)が残っています。
Tomasz Gandor 2016年

回答:


50

余分な先頭または末尾の空白を追加しないソリューション:

awk '{ for(i=4; i<NF; i++) printf "%s",$i OFS; if(NF) printf "%s",$NF; printf ORS}'

### Example ###
$ echo '1 2 3 4 5 6 7' |
  awk '{for(i=4;i<NF;i++)printf"%s",$i OFS;if(NF)printf"%s",$NF;printf ORS}' |
  tr ' ' '-'
4-5-6-7

Sudo_Oは三項演算子を使用してエレガントな改善を提案しますNF?ORS:OFS

$ echo '1 2 3 4 5 6 7' |
  awk '{ for(i=4; i<=NF; i++) printf "%s",$i (i==NF?ORS:OFS) }' |
  tr ' ' '-'
4-5-6-7

EdMortonは、フィールド間の元の空白を維持するソリューションを提供します。

$ echo '1   2 3 4   5    6 7' |
  awk '{ sub(/([^ ]+ +){3}/,"") }1' |
  tr ' ' '-'
4---5----6-7

BinaryZebraは、2つの素晴らしいソリューションも提供します
(これらのソリューションは、元の文字列の末尾のスペースも保持します)。

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ for ( i=1; i<=n; i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 ' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

コメントでlarsrによって与えられた解決策はほぼ正しいです:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for (i=3;i<=NF;i++) $(i-2)=$i; NF=NF-2; print $0}' | tr  ' ' '-'
3-4-5-6-7

これは、larsrソリューションの固定されパラメーター化されたバージョンです。

$ echo '1 2 3 4 5 6 7' | 
  awk '{for(i=n;i<=NF;i++)$(i-(n-1))=$i;NF=NF-(n-1);print $0}' n=4 | tr ' ' '-'
4-5-6-7

2013年9月より前の他のすべての回答は適切ですが、余分なスペースを追加します。


EdMortonの答えは私にはうまくいきませんでした(bash 4.1.2(1)-release、GNU Awk 3.1.7またはbash 3.2.25(1)-release、GNU Awk 3.1.5)。ここでは別の方法が見つかりましecho ' This is a test' | awk '{print substr($0, index($0,$3))}'
elysch

1
@elyschいいえ、それは一般的には機能しません。特定の入力値を指定すると機能するように見えます。私の回答の下にあるコメントの下に追加したコメントを参照してください。
Ed Morton

1
@fedorquiさん、こんにちは。私の答えは最初のものです。私の元の答えでは、他の答えが正しくなかった理由を説明していました(余分な先頭または末尾の空白)。一部の人々はコメント内の拡張を提案しています。私たちはOPにもっと正しい答えを選ぶように頼みました、そして彼/彼女は私のものを選びました。他の貢献者が私の回答を編集して、その回答を参照した後(履歴を参照)。分かりますか?私の回答の理解しやすさを改善するために、私に何をアドバイスしますか?乾杯;-)
olibre 2016

1
あなたは絶対に正しいです、そして私は私の誤解を非常に残念に思います。私は答えを速く読みましたが、元の答えに気付きませんでした(そうです、私は読みすぎました)。NF-1にループするための素敵なトリックを使用して回答自体を+1し、余分な空白を避けるために最後の要素を出力します。そしてまたごめんなさい!(将来の読者からの誤解を防ぐために、1日ほどで私のコメントを削除します)。
fedorqui 'SO stop harming' 2016

1
私は、ある種のヘッダーを使用します。<your answer>と、横線のルールの後に、「他の回答との比較」という大きなタイトルが続きます。どうやら、人々は「私のコードを欲しがる」ビジョンに短い答えを好む傾向にあるので、それ以外の場合は、別の答えにこの比較を移動します。)
fedorqui「をSO傷つける停止」

75
awk '{for(i=1;i<4;i++) $i="";print}' file

4
つまり、レコードのスペースの先頭をOFS処理しないので、これは先頭のままになりますNF
Chris Seymour

70

カットを使用

$ cut -f4-13 file

または、あなたがawkを主張し、$ 13が最後のフィールドである場合

$ awk '{$1=$2=$3="";print}' file

そうしないと

$ awk '{for(i=4;i<=13;i++)printf "%s ",$i;printf "\n"}' file

14
最後の例では、「13」よりも「NF」を使用する方が良いでしょう。
グレン・ジャックマン、2010

2
OPが決定する2つのシナリオ。13が最後のフィールドであれば、NFを使用しても問題ありません。そうでない場合は、13を使用するのが適切です。
ghostdog74 2010

3
2番目は、$ 0の最初から3つのOFSのコピーを削除する必要があります。を含むprintf "%s ",$iかどうか$iがわからないので、3番目はを使用したほうがよいでしょう%s。ただし、最後に余分なスペースが表示されます。
dubiousjim

38

これを試して:

awk '{ $1=""; $2=""; $3=""; print $0 }'

1
それがどれほどダイナミックであるかにより、これは素晴らしいです。最後に列を追加して、スクリプトを書き直すことはできません。
MinceMan 2012年

1
これは、質問があなたに対処しようとしている正確な問題を示しています。100番目のフィールドからの印刷についてはどうですか?あなたが対処しNFないので、あなたはリードを離れることに言及することに注意してくださいOFS
Chris Seymour

24

これを行う正しい方法は、REインターバルを使用することです。これにより、スキップするフィールドの数を簡単に指定でき、残りのフィールドのフィールド間スペースが保持されます。

たとえば、入力の形式を前提として、残りのフィールド間の間隔に影響を与えずに最初の3つのフィールドをスキップするには、この質問で議論しているように思われるのは次のとおりです。

$ echo '1   2 3 4   5    6' |
awk '{sub(/([^ ]+ +){3}/,"")}1'
4   5    6

先頭のスペースと非空白スペースに対応したいが、デフォルトのFSを使用する場合は、次のようになります。

$ echo '  1   2 3 4   5    6' |
awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"")}1'
4   5    6

文字セットで否定できないREであるFSがある場合、最初にそれを単一の文字に変換できます(RSはフィールド内に表示できないため、単一の文字である場合は、RSが理想的です。それ以外の場合は、SUBSEPを検討してください)。次に、RE間隔の置換を適用してから、OFSに変換します。たとえば、「。」のチェーンがフィールドを区切る場合:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,RS);sub("([^"RS"]+["RS"]+){3}","");gsub(RS,OFS)}1'
4 5 6

OFSが単一の文字であり、それが入力フィールドに表示できない場合は、次のように減らすことができます。

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,OFS); sub("([^"OFS"]+["OFS"]+){3}","")}1'
4 5 6

次に、フィールドを再割り当てするすべてのループベースのソリューションと同じ問題があります-FSはOFSに変換されます。それが問題である場合は、GNU awksのpatsplit()関数を調べる必要があります。


私にはうまくいきませんでした(bash 4.1.2(1)-release、GNU Awk 3.1.7またはbash 3.2.25(1)-release、GNU Awk 3.1.5)ここでは別の方法が見つかりましecho ' This is a test' | awk '{print substr($0, index($0,$3))}'
elysch

2
いいえ、$ 1または$ 2に$ 3が設定されている文字列が含まれている場合は失敗します。たとえばecho ' That is a test' | awk '{print substr($0, index($0,$3))}'、試してみると、a$ 3 が$ 1のa内部Thatと一致することがわかります。gawkの非常に古いバージョンでは、フラグでRE間隔を有効にする必要があります--re-interval
Ed Morton

2
あなたは正しい、気づかなかった。ところで、コメントありがとうございます。要素の数を指定するために "{}"で正規表現を使用したいと何度も思っていました。+1してください。
elysch 2014

1
1これは真の条件なので、現在のレコードを印刷するデフォルトのawkアクションを呼び出します。
Ed Morton

1
idkどれだけ標準的かについてですが、ここで答えを追加しました。
Ed Morton

10

現在、ほとんどすべての回答で、先頭のスペース、末尾のスペース、またはその他の区切り記号の問題が追加されています。セパレーターが空白で、出力セパレーターが1つのスペースである4番目のフィールドから選択するには、次のawkようにします。

awk '{for(i=4;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' file

開始フィールドをパラメーター化するには、次のようにします。

awk '{for(i=n;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' n=4 file

また、終了フィールド:

awk '{for(i=n;i<=m=(m>NF?NF:m);i++)printf "%s",$i (i==m?ORS:OFS)}' n=4 m=10 file

6
awk '{$1=$2=$3="";$0=$0;$1=$1}1'

入力

1 2 3 4 5 6 7

出力

4 5 6 7

4
echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) print $i }'

3
または、同じ行に配置するには、$ 3を$ 1などに割り当ててから、NFを適切な数のフィールドに変更します。echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) $(i-2)=$i; NF=NF-2; print $0 }'
larsr 2012

こんにちは@larsr。あなたが提案するコマンドラインは、単一の正解です。他のすべての回答は、余分なスペース(先頭または末尾)を追加します。新しい回答の中にコマンドラインを投稿してください。投票します;-)
olibre

1
こんにちは@sudo_Oです。コメントの中で彼が提案したコマンドラインについて、@ larsrと話していました。私はキプロコを理解する前に約5分を費やしました(誤解)。同意しORSます。@ Vetsinの回答では、フィールドの間に改行()が挿入されます。あなたのイニシアチブのためのブラボー(私はあなたの答えが好きです)。乾杯
olibre

3

printステートメントの使用を回避する別の方法:

 $ awk '{$1=$2=$3=""}sub("^"FS"*","")' file

awkでは、条件がtrueの場合、印刷がデフォルトのアクションです。


これには、@ lhfの回答にあるすべての問題があります。
Chris Seymour

非常に良いアイデア;)私の答えよりも優れています!(私は昨年あなたの答えをすでに賛成しました)乾杯
olibre

それは次のようになります。awk '{$1=$2=$3=""}sub("^"OFS"+","")' file$ 1、$ 2、$ 3つの内容を変更した後に残されているものであるOFSとして。

3

誰もプレーンシェルを提供していないなんて信じられません。

while read -r a b c d; do echo "$d"; done < file

同様のソリューションの+1 ...しかし、fileサイズが大きい場合(> 10-30KiB)、パフォーマンスに問題が生じる可能性があります。大きなファイルの場合、awkソリューションのパフォーマンスが向上します。
TrueY

3

オプション1〜3には、複数の空白に関する問題があります(ただし、簡単です)。これが、問題なく複数の空白を処理するオプション4と5を開発する理由です。もちろん、オプション4または5をn=0両方で使用すると、先頭の空白は保持され、n=0分割されません。

オプション1

シンプルなカットソリューション(単一の区切り文字で機能):

$ echo '1 2 3 4 5 6 7 8' | cut -d' ' -f4-
4 5 6 7 8

オプション2

awkの再計算を強制すると、追加された先行スペースの問題が解決される場合があります(awkの一部のバージョンで機能します)。

$ echo '1 2 3 4 5 6 7 8' | awk '{ $1=$2=$3="";$0=$0;} NF=NF'
4 5 6 7 8

オプション3

でフォーマットされた各フィールドを印刷するprintfと、より詳細に制御できます。

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=3 '{ for (i=n+1; i<=NF; i++){printf("%s%s",$i,i==NF?RS:OFS);} }'
4 5 6 7 8

ただし、以前のすべての回答は、フィールド間のすべてのFSをOFSに変更します。その解決策をいくつか作成しましょう。

オプション4

フィールドと区切り文字を削除するsubを含むループは移植性が高く、FSからOFSへの変更をトリガーしません。

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=3 '{ for(i=1;i<=n;i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 '
4   5   6 7     8

注意: "^ [" FS "] *"は、先行スペースを含む入力を受け入れます。

オプション5

次のようにgensub、GNU awk の関数を使用して、余分な先頭または末尾の空白を追加せず、既存の空白を保持するソリューションを構築することはかなり可能です。

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }'
4   5   6 7     8 

また、カウントを指定してフィールドリストを交換するために使用することもできますn

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=3 '{ a=gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1);
                b=gensub("^(.*)("a")","\\1",1);
                print "|"a"|","!"b"!";
               }'
|4   5   6 7     8  | !    1    2  3     !

もちろん、そのような場合、OFSは行の両方の部分を分離するために使用され、フィールドの末尾の空白は引き続き印刷されます。

注1: ["FS"]*入力行で先行スペースを許可するために使用されます。


こんにちはBZあなたの答えはいいです。ただし、オプション3はスペースで始まる文字列(たとえば" 1 2 3 4 5 6 7 8 ")では機能しません。オプション4は適切ですが、スペースで始まる文字列を使用して先頭のスペースを残します。これが修正可能であると思いますか?echo " 1 2 3 4 5 6 7 8 " | your awk script | sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'先頭/中央/末尾のスペースを確認するためにコマンドを使用できます...乾杯;)
olibre

@olibre様 オプション3が空白で失敗するのは、オプション4と5を開発する理由です。オプション4は、入力に先行スペースがあり nが0に設定されている場合(n = 0)にのみ先行スペースを残します。フィールドが選択されていない場合(IMOを修正するものがない場合)は、正しい答えだと思います。乾杯。

大丈夫。追加情報をありがとう:-)これらの追加情報を提供して、回答を改善してください:-)乾杯
olibre

パーフェクト:-)ユーザーが無効にされているなんて同情:-(
olibre

1

Cutには--complementフラグがあり、列を簡単に(かつ高速に)削除できます。結果の構文は、実行したいことと類似しており、ソリューションを読みやすく/理解しやすくします。補数は、連続していない列を削除する場合にも機能します。

$ foo='1 2 3 %s 5 6 7'
$ echo "$foo" | cut --complement -d' ' -f1-3
%s 5 6 7
$

あなたの答えをもっと説明してもらえますか?
ズールー

上記の編集は理解に役立ちますか?ポイントは、カットの補完フラグを使用することです。このソリューションは、AWKまたはperlベースのソリューションよりも高速で簡潔な実装である必要があります。また、任意の列をカットすることができます。
マイケルバック

1

先頭または末尾の空白を追加しないPerlソリューション:

perl -lane 'splice @F,0,3; print join " ",@F' file

perl @Fautosplit配列はインデックスで始まり、0awkフィールドは$1


カンマ区切りデータのPerlソリューション:

perl -F, -lane 'splice @F,0,3; print join ",",@F' file

Pythonソリューション:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[3:]) + '\n') for line in sys.stdin]" < file


0

私にとって、リクエストに対する最もコンパクトで準拠したソリューションは

$ a='1   2\t \t3     4   5   6 7 \t 8\t '; 
$ echo -e "$a" | awk -v n=3 '{while (i<n) {i++; sub($1 FS"*", "")}; print $0}'

また、インスタンスファイルfoo.txtのように処理する行がさらにある場合は、iを0にリセットすることを忘れないでください。

$ awk -v n=3 '{i=0; while (i<n) {i++; sub($1 FS"*", "")}; print $0}' foo.txt

フォーラムに感謝します。


0

最初の非常に賛成されたが間違った答えに悩まされたので、そこに返信を書くのに十分であることを発見しました。答えをそれほど複雑にする理由が見当たらないため、提案された解決策は好きではありません。

$ 5の後にIPアドレスを入力すると、テキストが増える場合とない場合があります。$ 5より後のものがあれば、IPアドレスから行末まですべてが必要です。私の場合、これは実際にはawkプログラムではなくawkワンライナーなので、awkが問題を解決する必要があります。古い見栄えがよく、最も賛成されているが完全に間違っている答えを使用して最初の4つのフィールドを削除しようとすると、次のようになります。

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

それは間違って役に立たない応答を吐き出します(私は[]を追加して説明します):

[    37.244.182.218 one two three]

代わりに、カットポイントとawkが必要になるまで列の幅が固定されている場合、正しい簡単な答えは次のとおりです。

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{printf "[%s]\n", substr($0,28)}'

これにより、必要な出力が生成されます。

[37.244.182.218 one two three]

0

私はこの他の可能性を見つけました、多分それはまた役に立つかもしれません...

awk 'BEGIN {OFS=ORS="\t" }; {for(i=1; i<14; i++) print $i " "; print $NF "\n" }' your_file

注: 1.表形式データの場合、列$ 1から$ 14まで


0

カットを使用:

cut -d <The character between characters> -f <number of first column>,<number of last column> <file name>

例:含まれている場合file1car.is.nice.equal.bmw

実行:cut -d . -f1,3 file1 印刷されますcar.is.nice


あなたの解決策は逆かもしれないようです。質問のタイトルを確認してください最初の3列を除くすべてを印刷
Stefan Crain

-1

これは以前の回答のいくつかからそれほど遠くないですが、いくつかの問題を解決します:

cols.sh

#!/bin/bash
awk -v s=$1 '{for(i=s; i<=NF;i++) printf "%-5s", $i; print "" }'

これは、開始列となる引数で呼び出すことができます。

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 3 
3    4    5    6    7    8    9    10   11   12   13   14

または:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 
7    8    9    10   11   12   13   14

これは1インデックスです。インデックスをゼロにしたい場合は、i=s + 1代わりに使用してください。

さらに、開始インデックス終了インデックスの引数が必要な場合は、ファイルを次のように変更します。

#!/bin/bash
awk -v s=$1 -v e=$2 '{for(i=s; i<=e;i++) printf "%-5s", $i; print "" }'

例えば:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 9 
7    8    9

%-5s整列5文字の幅の列として結果; これで十分でない場合は、数を増やすか、%s配置を気にしない場合は代わりに(スペースを入れて)使用します。


-1

%問題を回避するAWK printfベースのソリューションであり、印刷する列が4つ未満の場合、何も返さない(戻り文字がない)という点でユニークです。

awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'

テスト:

$ x='1 2 3 %s 4 5 6'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
%s 4 5 6
$ x='1 2 3'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$ x='1 2 3 '
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.