Linux中的文本文件操作

Linux中的文本文件操作,linux,bash,awk,sed,Linux,Bash,Awk,Sed,我在一个文件夹中有一堆CSV文件(OS:Ubuntu)。它们都在同一个结构上。超过2k列(这就是我们获得它的原因)。第一列是ID 我不能使用SQL(不管为什么),所以我想我需要使用bash命令,比如awk、cut、sed,我对它们有基本的了解 我需要做以下工作: 运行文件(就像文件合并为一个文件):对于每个偶数列,检查它是否有一个等于0-->的不同值。如果有,请删除该列和下一列。 此外,我需要将删除列的索引打印到一个新文件中 范例 file_1: 2231, 0, 5, 0, 9, 0, 9,

我在一个文件夹中有一堆CSV文件(OS:Ubuntu)。它们都在同一个结构上。超过2k列(这就是我们获得它的原因)。第一列是ID

我不能使用SQL(不管为什么),所以我想我需要使用bash命令,比如
awk
cut
sed
,我对它们有基本的了解

我需要做以下工作: 运行文件(就像文件合并为一个文件):对于每个偶数列,检查它是否有一个等于
0
-->的不同值。如果有,请删除该列和下一列。 此外,我需要将删除列的索引打印到一个新文件中

范例

file_1:
2231, 0, 5, 0, 9, 0, 9, 3, 3
1322, 0, 5, 0, 1, 0, 9, 2, 5
1233, 5, 5, 0, 3, 0, 9, 4, 6
1543, 2, 5, 0, 4, 0, 9, 6, 1
2341, 0, 5, 0, 7, 0, 9, 0, 2

files_2:
1322, 0, 5, 0, 3, 0, 9, 1, 2
1432, 0, 5, 0, 0, 0, 9, 3, 7
1434, 0, 5, 0, 8, 0, 9, 1, 4
1132, 0, 5, 0, 4, 0, 9, 3, 5
1434, 0, 5, 0, 7, 0, 9, 1, 0
预期结果:

Removed index columns file: 4, 5, 6, 7

    file_1 content:
    2231, 0, 5, 3, 3
    1322, 0, 5, 2, 5
    1233, 5, 5, 4, 6
    1543, 2, 5, 6, 1
    2341, 0, 5, 0, 2

    files_2 content:
    1322, 0, 5, 1, 2
    1432, 0, 5, 3, 7
    1434, 0, 5, 1, 4
    1132, 0, 5, 3, 5
    1434, 0, 5, 1, 0

使用这些bash命令中的一个可以做到这一点吗?如果是,怎么做?任何其他解决方案都很好,但我更喜欢bash命令。

您可以使用awk跳过这些列并将其全部置零:

awk 'BEGIN { FS=OFS=", " }
NR==1 {
   for (i=2; i<=NF; i+=2)
      a[i]
} FNR==NR {
   for (i=2; i<=NF; i+=2)
      if (i in a && $i>0)
         delete a[i];
   next
} {
   for (i=1; i<=NF; i++)
      if (!(i in a))
         printf "%s%s", $i, (i<NF)? OFS : RS
}' file1 file1
它使用数组
a
来保持偶数列,而应该从输出中跳过

在第1遍中,当:

NR==1   # will run for first row to create an array a with even # of columns as index
FNR==NR # block will run for 1st pass of the file. It will delete entries from array a
        # if current value is greater than zero.
{...}   # in the 2nd pass we iterate each column and print if col is not in array a

更新:

2231, 0, 5, 9, 9, 3, 3
1322, 0, 5, 1, 9, 2, 5
1233, 5, 5, 3, 9, 4, 6
1543, 2, 5, 4, 9, 6, 1
2341, 0, 5, 7, 9, 0, 2
根据下面的评论

awk 'BEGIN{FS=OFS=","}
FNR==NR {
   for (i=1; i<=NF; i++)
      sums[i] += $i;
   ++r;
   next
} {
   for (i=1; i<=NF; i++)
      if (sums[i] > 0 && sums[i+1]>0 && sums[i] != 100*r)
         printf "%s%s", (i>1)?OFS:"", $i;
      print ""
}' file file
awk'BEGIN{FS=OFS=“,”}
FNR==NR{
对于(i=1;i0&&sums[i]!=100*r)
printf“%s%s”,(i>1)OFS:,$i;
打印“”
}'文件

您可以使用awk将这些列全部用零跳过:

awk 'BEGIN { FS=OFS=", " }
NR==1 {
   for (i=2; i<=NF; i+=2)
      a[i]
} FNR==NR {
   for (i=2; i<=NF; i+=2)
      if (i in a && $i>0)
         delete a[i];
   next
} {
   for (i=1; i<=NF; i++)
      if (!(i in a))
         printf "%s%s", $i, (i<NF)? OFS : RS
}' file1 file1
它使用数组
a
来保持偶数列,而应该从输出中跳过

在第1遍中,当:

NR==1   # will run for first row to create an array a with even # of columns as index
FNR==NR # block will run for 1st pass of the file. It will delete entries from array a
        # if current value is greater than zero.
{...}   # in the 2nd pass we iterate each column and print if col is not in array a

更新:

2231, 0, 5, 9, 9, 3, 3
1322, 0, 5, 1, 9, 2, 5
1233, 5, 5, 3, 9, 4, 6
1543, 2, 5, 4, 9, 6, 1
2341, 0, 5, 7, 9, 0, 2
根据下面的评论

awk 'BEGIN{FS=OFS=","}
FNR==NR {
   for (i=1; i<=NF; i++)
      sums[i] += $i;
   ++r;
   next
} {
   for (i=1; i<=NF; i++)
      if (sums[i] > 0 && sums[i+1]>0 && sums[i] != 100*r)
         printf "%s%s", (i>1)?OFS:"", $i;
      print ""
}' file file
awk'BEGIN{FS=OFS=“,”}
FNR==NR{
对于(i=1;i0&&sums[i]!=100*r)
printf“%s%s”,(i>1)OFS:,$i;
打印“”
}'文件

如果不查看每个文件的每一行,就无法判断要删除哪些列

我选择perl来处理字符串和列表。可以在awk中执行此操作,但awk只有关联数组。我认为在awk中过滤掉杀戮候选人会很烦人

如果您已经了解Python,请使用它。这主要是抛出列表,而不是字符串,python擅长于此

两次通过的算法可以如下所示:

# pseudo-perl, untested code
#!/usr/bin/perl -w
$line_scalar = <>;  # read the first line;
@line = split /\s*,\s*/, $list_scalar;
killcols= indices of columns that are '0' in @line;

while(<>) {
    @line = split /\s*,\s*/, $_;

    # filter out candidates that aren't zero in this line.  (string '0' is false in perl)
    @killcols = grep { ! $line[$_] } @killcols;
}
#伪perl,未经测试的代码
#!/usr/bin/perl-w
$line_标量=;#读第一行;
@line=split/\s*,\s*/,$list\u标量;
killcols=在@line中为“0”的列的索引;
while(){
@line=split/\s*,\s*/,$;
#筛选出此行中不是零的候选项。(字符串“0”在perl中为false)
@killcols=grep{!$line[$\u]}@killcols;
}
由于要删除的列数可能很小,候选列表将很快变短。只检查每行中剩余的候选项应该比查看每个字段以找到零的候选项要快得多。你仍然需要阅读和分割每一行,但这应该是好的。这比将整个内容读入内存,然后针对每一列在每一行上循环一次要好得多

如果您的数据集太大,无法保存在内存中,只要知道要删除哪些列,就可以重新读取并重新拆分输入文件

如果数据集不是很大,可以在读取行时将行存储在内存中,避免再次读取/拆分。不过,您必须跟踪哪一行与哪一个文件对应。在perl中,可能最好为每个文件提供一个解析行列表。要跟踪哪个列表和哪个文件,请将LOL保存在散列中

在perl中,普通列表变平

我的perl已经生锈了,所以我可能弄错了

#未经测试的代码,很确定存在小错误
#你会有问题的。还是用python重写吧
...  # 从循环外部的第一行获取列数
while(){
@line=split/\s*,\s*/,$;
按{$lines{$ARGV}},[@line];#将解析后的字段附加到当前文件的行列表中
#筛选出此行中不是零的候选项。(字符串“0”在perl中为false)
@killcols=grep{!$line[$\u]}@killcols;
}
#我的@keepcols;
#foreach my$col(1..$maxcol){
#按@keepcols,$col if!grep{$col==$\}@killcols;
#}
#IDK,如果这是反转集合的最有效方法
#在任何情况下,实际存储所有列编号*的列表以*打印
#可能比在@killcol之间的范围内循环要慢。
每个我的$f(@ARGV){
打开输出文件“>”、“$f.new”或“打开$f.new:$!”时出错”;
foreach my$lref(@$line{$f}){
我的$printcol=0;
每个我的$kcol(@killcols){
#FIXME:分隔符逻辑可能不是防弹的,例如,当最后一个终止列=maxcol时
打印(输出文件、连接(“,”,$lref->[$printcol..$kcol-1]);
打印输出文件“,”;
$printcol=$kcol+1;#跳过终止列
}
打印(输出文件,连接(“,”,$lref->[$printcol..$maxcol]),“\n”);
#最后一个没有尾随“,”。
#可以将$maxcol+1附加到@killcols,因为我认为分隔符逻辑无论如何都需要修复。
}
关闭输出口;
#关闭“$f.new”,如果没有任何I/O错误,请将其重命名为“$f”
}

如果不查看每个文件的每一行,就无法判断要删除哪些列

我选择perl来处理字符串和列表。可以在awk中执行此操作,但awk只有关联数组。我认为在awk中过滤掉杀戮候选人会很烦人

如果您已经了解Python,请使用它。这主要是抛出列表,而不是字符串,python擅长于此

两次通过的算法可以如下所示:

# pseudo-perl, untested code
#!/usr/bin/perl -w
$line_scalar = <>;  # read the first line;
@line = split /\s*,\s*/, $list_scalar;
killcols= indices of columns that are '0' in @line;

while(<>) {
    @line = split /\s*,\s*/, $_;

    # filter out candidates that aren't zero in this line.  (string '0' is false in perl)
    @killcols = grep { ! $line[$_] } @killcols;
}
#伪perl,未经测试的代码
#!/usr/bin/perl-w
$line_标量=;#读第一行;
@line=split/\s*,\s*/,$list\u标量;
killcols=在@line中为“0”的列的索引;
while(){
@line=split/\s*,\s*/,$;
#筛选出此行中不是零的候选项。(字符串“0”在perl中为false)
@killcols=grep