删除包含特定文本的列

删除包含特定文本的列

我正在寻找一个选项来删除具有特定文本的列,例如

“滴滴”

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)}}

    这将扫描第一行中的所有列。我们将带有(减一)的列号保存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每次循环 都会加一。在每个循环中i+=1+(i==n)递增i1,除非i==n在这种情况下i递增 2。这具有跳过右列的效果。

    printf "%s%s",$i,i==m?ORS:OFS打印列i,后跟列分隔符OFS或行分隔符 ,ORS具体取决于是否i是最后一列。

多条线

对于那些喜欢多行编写这样的命令的人:

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 个循环完成以避免混淆,但只能在一个循环中完成

相关内容