特定のテキストを含む列を削除する

特定のテキストを含む列を削除する

次の特定のテキストを含む列を削除するオプションを探しています。

「ディディ」

aaa bbb ccc ddd eee fff
1   2   3   4   5   6
2   3   4   5   6   0

したがって、出力は次のようになります。

aaa bbb ccc eee fff
1   2   3   5   6
2   3   4   6   0

私は列4を削除し、同じことを行う簡単なオプションがあることを知っていますが、私の* .csvファイルはソートされません。どんなアイデアがありますか?

答え1

sed正しいツールではありません。努力するawk

$ awk -v OFS='\t' 'NR==1{for (i=1;i<=NF;i++)if ($i=="ddd"){n=i-1;m=NF-(i==NF)}} {for(i=1;i<=NF;i+=1+(i==n))printf "%s%s",$i,i==m?ORS:OFS}' file
aaa     bbb     ccc     eee     fff
1       2       3       5       6
2       3       4       6       0

削除したい文字列(ddd この例では)が最初の行のフィールドとして表示されるとします。

どのように動作しますか?

  • -v OFS='\t'

    これにより、出力フィールド区切り文字がタブに設定されます。他のものを使用している場合は、この設定を変更してください。

  • NR==1{for (i=1;i<=NF;i++)if ($i=="ddd"){n=i-1;m=NF-(i==NF)}}

    これにより、最初の行のすべての列が検索されます。 (マイナス1)として列番号をddd変数に保存しますn

    m最後の列の番号にも設定されますが、i最後の列の場合に設定されますNF-1

  • for(i=1;i<=NF;i+=1+(i==n))printf "%s%s",$i,i==m?ORS:OFS

    dddこれにより、最初の行に表示されるフィールドがスキップされ、すべてのフィールドが印刷されます。

    i+=1iループを通過するたびに1ずつ増加します。各ループで1i+=1+(i==n)ずつ増加しますi。ただし、i==nこの場合はi2ずつ増加します。これは右側の列をスキップする効果があります。

    printf "%s%s",$i,i==m?ORS:OFS最後の列であるかどうかに応じて、i列区切り文字OFSまたは行区切り文字の後に続く列を印刷します。ORSi

複数行

このようなコマンドを複数行で書くことを好む人のために:

awk -v OFS='\t' '

NR==1{
    for (i=1;i<=NF;i++)
        if ($i=="ddd") {
            n=i-1
            m=NF-(i==NF)
        }
    }

{
    for(i=1;i<=NF;i+=1+(i==n))
        printf "%s%s",$i,i==m?ORS:OFS
}

' file

カンマ区切りファイルの使用

入力と出力をコンマで区切るには、入力フィールド区切り記号(使用-F)と出力フィールド区切り記号を変更する必要があります。たとえば、次の入力ファイルを考えてみましょう。

$ cat file2
aaa,bbb,ccc,ddd,eee,fff
1,2,3,4,5,6
2,3,4,5,6,0

次に、次を使用します。

$ awk -F, -v OFS=, 'NR==1{for (i=1;i<=NF;i++)if ($i=="ddd"){n=i-1;m=NF-(i==NF)}} {for(i=1;i<=NF;i+=1+(i==n))printf "%s%s",$i,i==m?ORS:OFS}' file2
aaa,bbb,ccc,eee,fff
1,2,3,5,6
2,3,4,6,0

答え2

これは(ほとんどの場合)可能ですが、sed他のツールを使用するよりも簡単であるかどうか疑問です。最も簡単な方法は、まず目的のフィールド番号を取得し、ファイルを介して残りを印刷することです。たとえば、Perlでは次のようになります。

$ perl -lane 'if($.==1){for(0..$#F){$d=$_ if $F[$_] eq "ddd"}} 
              print "@F[0..$d-1] @F[$d+1..$#F]"' file
aaa bbb ccc eee fff
1 2 3 5 6
2 3 4 6 0

しかし、これは形式を混乱させます。重要な場合John1024の答えを使う代わりに。

答え3

#!/bin/bash
read -a header <file                 # read first line into array "header"
for i in ${!header[@]}               # iterate through array indexes
do
    if [ "${header[i]}" = "ddd" ]    # find column equal the pattern
    then
        n=$[++i]                     # put column number in variable "n"
        break
    fi
done
# print all column except found delimited by <TAB>
cut --complement -f ${n} < <(tr -s ' ' '\t' <6)

でも欲しいなら「ただsed「(最適ではなくても)

sed "s/\S\+\s*//$(sed 's/\s\+/\n/g;1q' file | sed -n '/ddd/=')" file

\S\+\s*- 平均1列(空白以外の文字の後に空白がある)。
s/<pattern>//<number>- 交換のみ可能数字初めて登場模様。 - 最初の行(タイトル)でのみ、
s/\s\+/\n/gすべての空白または空白ブロックをewlineに置き換えます。したがって、ヘッダー列を行に変換します。 - 行番号を出力してください。\n
1q
sed -n '/ddd/=模様存在する==列数

答え4

awk -F "\t" -v "Pat=ddd" 'NR == 1 {for( i = 1; i <= NF; i++) Take[ i] = (Pat != $i)} {for ( i = 1; i <= NF; i++) if( Take[ i]) printf $i FS; print ""}' YourFile
  • 除外されていないフィールドの印刷
  • 入力と同じ区切り文字を使用してください(-F "\t")。
  • ()を除外するには、パターン選択に変数を使用します-v "Pat=ddd"。多くの選択をしたい場合は、正規表現の選択を簡単に変更できます。
  • 混乱を避けるために、2つのループで完了しますが、1つのループでのみ実行されます。

関連情報