迭代和有条件地删除文件中的行

时间:2014-09-28 02:44:58

标签: macos bash random awk sed

简介

我有一个名为data.dat的文件,其结构如下:

    1:    67:     1 :s
    1:   315:     1 :s
    1:   648:     1 :ns
    1:   799:     1 :s
    1:   809:     1 :s
    1:   997:     1 :ns
    2:    32:     1 :s

算法

我正在寻找的算法是:

  1. 在此文件中生成1到1行之间的随机数。
  2. 如果第四列是“s”,则删除该行。
  3. 否则生成另一个随机数并重复此操作,直到行数达到某个值。
  4. 技术概念

    虽然技术概念与此算法无关,但我尝试解释该问题。数据显示网络的连接表。该算法允许我们在不同的初始条件下运行它并研究这些网络的一般属性。特别是,由于删除债券的随机性,这些网络中的任何共同行为都可以被解释为基本规律。

    更新:在每个步骤中生成随机数的另一个好理由是,在删除每一行后,其余行的s / ns属性可能可以改变。

    代码

    这是我到目前为止的代码:

    #!/bin/bash
    # bash in OSX
    
    While ((#there is at least 1 s in the fourth column)); do
    
       LEN=$(grep -c "." data.dat)  # number of lines
       RAND=$((RANDOM%${LEN}+1))    # generating random number
    
       if [[awk -F, "NR==$RAND" 'data.dat' |  cut -d ':' -f 4- == "s"]]; then
             sed '$RANDd' data.txt
       else
             #go back and produce another random
    done
    exit
    

    我尝试使用awk -F, "NR==$RAND" 'data.dat' | cut -d ':' -f 4-找到第四列,并按sed '$RANDd' data.txt删除该行。

    问题

    1. 我应该如何检查文件中是否有s对?
    2. 我不确定if中的条件是否正确。
    3. 此外,我不知道如何在else之后强行循环以返回生成另一个随机数。
    4. 谢谢,

      我非常感谢你的帮助。

3 个答案:

答案 0 :(得分:0)

#!/usr/bin/env perl

# usage: $ excise.pl < data.dat > smaller_data.dat

my $sampleLimit = 10; # sample up to ten lines before printing output

my $dataRef;
my $flagRef;
while (<>) {
    chomp;
    push (@{$dataRef}, $_);
    push (@{$flagRef}, 1);
}
my $lineCount = scalar @elems;
my $sampleIndex = 0;
while ($sampleIndex < $sampleLimit) {
    my $sampleLineIndex = int(rand($lineCount));
    my @sampleElems = split("\t", $dataRef->[$sampleLineIndex];
    if ($sampleElems[3] == "s") {
        $flagRef->[$sampleLineIndex] = 0;
    }
    $sampleIndex++;
}
# print data.dat to standard output, minus any sampled lines that had an 's' in them
foreach my $lineIndex (0..(scalar @{$dataRef} - 1)) {
    if ($flagRef->[$lineIndex] == 1) {
        print STDOUT $dataRef->[$lineIndex]."\n";
    }
}

答案 1 :(得分:0)

就个人而言,我建议不要在bash中这样做,除非你绝对没有选择。

这是你在Perl中做到的另一种方式(功能与Alex's answer非常相似,但有点简单):

use strict;
use warnings;

my $filename = shift;
open my $fh, "<", $filename or die "could not open $filename: $!";
chomp (my @lines = <$fh>);

my $sample = 0;
my $max_samples = 10;
while ($sample++ < $max_samples) {
    my $line_no = int rand @lines;
    my $line = $lines[$line_no];
    if ($line =~ /:s\s*$/) {
        splice @lines, $line_no, 1;
    }
}

print "$_\n" for @lines;

用法:perl script.pl data.dat

将文件读入数组@lines。从数组中选择一个随机行,如果它以:s结尾(后跟任意数量的空格),则将其删除。最后打印剩余的行。

这可以满足您的需求,但我应该警告您,依赖任何语言的内置随机数生成器并不是获得统计上显着结论的好方法。如果您需要高质量的随机数,则应考虑使用Math::Random::MT::Perl之类的模块来生成它们,而不是使用内置的rand

答案 2 :(得分:0)

NumLine=$( grep -c "" data.dat )
while [ ${NumLine} -gt ${TargetLine} ]
 do
   # echo "Line at start: ${NumLine}"

   RndLine=$(( ( ${RANDOM} % ${NumLine} ) + 1 ))
   RndValue="$( echo "      ${RANDOM}" | sed 's/.*\(.\{6\}\)$/\1/' )"

   sed "${RndLine} {
          s/^\([^:]*:\)[^:]*\(:.*:ns$\)/\1${RndValue}\2/
          t
          d
          }" data.dat > /tmp/data.dat
   mv /tmp/data.dat data.dat
   NumLine=$( grep -c "" data.dat )

   #cat data.dat
   #echo "- Next Iteration -------"
 done

在AIX上测试(所以不是GNU sed)。在Linux下,使用--posix作为sed选项,在这种情况下,您可以使用-i代替临时文件+重定向+移动

不要忘记RANDOM不是真正的RANDOM所以基于非随机值的网络行为研究无法反映特定情况下的现实