テーブルがあります。
M - A A -
- A G - -
M - - - G
私は以下を実行したい:最初の行の列に「-」が含まれている場合は、その列の印刷をスキップします。
予想される出力は次のとおりです。
M A A
- G -
M - -
私は成功せずにこのようなことを試しました。
awk 'NR==1 && $i!="-" {print $i}'
コマンドを修正する方法を知っている人はいますか?
答え1
の変種エドモートンの答えは、フィールド番号によって最初の行にないフィールドを記憶し、新しいレコードを印刷する前に、配列に格納されているインデックスに-
基づいて入力内の各レコードを再設定します。out
FNR == 1 {
for (i = 1; i <= NF; ++i)
if ($i != "-") out[++nf] = i
}
{
for (i = 1; i <= nf; ++i)
a[i] = $(out[i])
$0 = ""
for (i = 1; i <= nf; ++i)
$i = a[i]
print
}
ここでは、読みやすくするためにいくつかの効率を犠牲にして、2番目のブロックの単一ループで必須フィールドを印刷するのではなく、別のループでレコードを再構築しました。
テスト:
$ awk -f script.awk file
M A A
- G -
M - -
タブを出力フィールド区切り文字として使用して実行します。
$ awk -v OFS='\t' -f script.awk file
M A A
- G -
M - -
入力データがタブで区切られているかどうかによって、コードの行が少し長すぎます。
$ cut -f "$(awk -v OFS=',' '{ nf=split($0,a); $0=""; for (i=1; i<=nf; ++i) if (a[i]!="-") $(++NF)=i; print; exit }' file)" file
M A A
- G -
M - -
awk
これは出力に使用されます。フィールド番号-
カンマで区切られたリストで、最初の行にはありません。その後、そのリストがリストに渡され、cut -f
実際にファイルのデータが出力されます。ファイル名(ここで省略file
)はコマンドラインに2回与えられます。一度はについて、awk
もう一度はについて再提供されますcut
。
答え2
$ cat tst.awk
NR == 1 {
for (i=1; i<=NF; i++) {
if ($i != "-") {
f[++numOutFlds] = i
}
}
}
{
for (i=1; i<=numOutFlds; i++) {
printf "%s%s", $(f[i]), (i<numOutFlds ? OFS : ORS)
}
}
$ awk -f tst.awk file
M A A
- G -
M - -
答え3
awk
入力レコード(行)およびファイルに対して暗黙的にループを実行しますが、明示的に実行する必要があるフィールドに対してループを実行しません。あなたの場合、最初の行(ヘッダー行)のフィールドを繰り返して含める列を決定し、それを繰り返す必要があります。すべて行(タイトルと非タイトル)に、その行に必要な列が含まれています。
次のヘッダーフィールドを探しているかどうかはわかりません。同じ(文字列)「-」または次のように渡すこともできます。(サブ)文字列。また、フィールド区切り文字として複数の空白ではなく単一のタブがあるとします。これはより退屈で、投稿と視覚的に区別されません。
awk -F"\t" 'NR==1{for(i=1;i<=NF;i++)s[i]=$i!="-"} {x="";for(i=1;i<=NF;i++)if(s[i])x=x FS $i;print substr(x,2)}'
# for _matches_ "-" instead of _equals_ "-" change $i!="-" to $i!~/-/
# note if a nonheader line has more fields than the header did,
# all extra fields are nonselected (as if their header field was/matched -)
# or (re)use the flags for both what to include _and_ when to terminate the line
awk -F"\t" 'NR==1{t=RS;for(i=NF;i;i--)if(s[i]=($i!="-"?t:""))t=FS} {for(i=1;i<=NF;i++)if(s[i])printf "%s%s",$i,s[i]}'
# some people may consider this too clever
答え4
を使用してこれを行うことができますsed
。コードは拡張正規表現モードでGNU sedを使用していますが、これは単なる治療法ですbackslashitis
。
方法は、最初の行からマップを生成することです。保持されるフィールドはxにマッピングされ、他のフィールドはダッシュにマッピングされます。この地図を貨物室に保管してください。
次に、すべての行にこのマップを追加し、BOLにマーカーを配置します。
ループ内で\ n -が表示され、マーカーが次のフィールドに移動すると、現在の行の先行フィールドは引き続き削除されます。
このマーカーが現在の行と予約済みスペースの間の改行文字と競合すると、ループは終了します(Gコマンドのため)。
$ sed -Ee '
1{
h
y/-/\n/
s/\S+/x/g;s/[[:blank:]]+//g
y/\n/-/
x
}
G;s/^/\n/
:a
s/\n(\S+\s*)(.*\n)x/\1\n\2/
s/\n(\S+\s*)(.*\n)-/\n\2/
/\n\n/!ba
s/\s+$//
' file
結果
M A A
- G -
M - -