我正在寻找一个选项来删除具有特定文本的列,例如
“滴滴”
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+=1
i
每次循环 都会加一。在每个循环中i+=1+(i==n)
递增i
1,除非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 个循环完成以避免混淆,但只能在一个循环中完成