在bash中删除文本文件中的单个元素

时间:2012-04-17 18:43:46

标签: regex bash sed text-files

基本上我所拥有的是一个文本文件(file.txt),它包含数字行(行不一定是相同的长度),例如。


1 2 3 4
5 6 7 8
9 10 11 12 13

我需要做的是编写新文件,删除每个号码,一次删除一个,例如更换第一个新文件将包含


2 3 4< ---第1个元素被移除
5 6 7 8
9 10 11 12 13

并且第7个文件将包含


1 2 3 4 在此处移除了5 6 8 <---第7个元素
9 10 11 12 13

为了生成这些,我循环遍历每一行,然后遍历每一行中的每个元素。例如。对于第7个文件,我删除第二行的第三个元素,我试图通过读取行​​,删除相应的元素,然后重新插入这个新行


$ lineNo是2(第二行)
$ line是5 6 7 8
有了剪切,我删除第三个号码,制作$ newline 5 6 8

然后我尝试用$ newline使用sed替换file.txt中的$ lineNo行:
sed -n'$ lineNo s /.*/'$ newline'/'&gt; file.txt的

这完全不起作用。我收到一个错误
sed:无法读取25.780000:没有这样的文件或目录

(其中25.780000是我的文本文件中的数字。看起来它正在尝试使用$ newline来读取文件或其他内容)
我有理由怀疑我说明要替换的哪条线路不起作用:(

我的问题是,a)有没有更好的方法来做到这一点而不是sed,b)如果sed是要走的路,我做错了什么?

谢谢!

4 个答案:

答案 0 :(得分:3)

filename=file.txt
i=1
while [[ -s $filename ]]; do
    new=file_$i.txt
    awk 'NR==1 {if (NF==1) next; else sub(/^[^ ]+ /, "")} 1' $filename > $new
    ((i++))
    filename=$new
done

这会在每个新文件的第一行的开头留一个空格,当一行变空时,该行将被删除。当最后生成的文件为空时,循环结束。


由于要求澄清,

更新

words=$(wc -w < file.txt)
for ((i=1; i<=words; i++)); do 
    awk -v n=$i '
        words < n && n <= words+NF {$(n-words) = "" }
        {words += NF; print}
    ' file.txt > file_$i.txt
done

答案 1 :(得分:3)

除非我误解了这个问题,否则以下内容应该有效,但如果你的文件很大,它会很慢:

#! /bin/bash

remove_by_value()
{
  local TO_REMOVE=$1

  while read line; do 
    out=
    for word in $line; do [ "$word" = "$TO_REMOVE" ] || out="$out $word"; done
    echo "${out/ }"
  done < $2
}

remove_by_position()
{
  local NTH=$1

  while read line; do
    out=
    for word in $line; do
      ((--NTH == 0)) || out="$out $word"
    done
    echo "${out/ }"
  done < $2
}

FILE=$1
shift  
for number; do
  echo "Removing $number"
  remove_by_position $number "$FILE"
done

这会将所有输出转储到stdout,但更改它应该是微不足道的,因此每个删除的数字的输出都会被重定向(例如,使用remove_by_position $number $FILE > $FILE.$$ && mv $FILE.$$ $FILE.$number并正确引用)。运行它,比方说,

$ bash script.sh file.txt $(seq 11)

答案 2 :(得分:1)

我不得不承认,我对其他解决方案的简短有点惊讶。

#!/bin/bash
#
file=$1
lines=$(cat $file | wc -l) 
out=0

dropFromLine () {
    file=$1
    row=$2
    to=$((row-1))
    from=$((row+1))
    linecontent=($(sed -n "${row}p" $file))
    # echo "    linecontent: " ${linecontent[@]}
    linelen=${#linecontent[@]}
    # echo "    linelength: " $linelen
    for n in $(seq 0 $linelen) 
    do
        ( 
        if [[ $row > 1 ]] ; then sed -n "1,${to}p" $file ;fi
        for i in $(seq 0 $linelen) 
        do
            if [[ $n != $i ]]
            then
                echo -n ${linecontent[$i]}" " 
            fi
        done
        echo 
        # echo "mod - drop " ${linecontent[$n]}
        sed -n "$from,${lines}p" $file 
        ) > outfile-${out}.txt
        out=$((out+1))
    done 
}

for row in $(seq 1 $lines)
do 
    dropFromLine $file $row 
done

调用:

./dropFromRow.sh num.dat

num.dat:

1 2 3 4
5 6 7 8
9 10 11

结果:

outfile-0  outfile-10  outfile-12  outfile-2  outfile-4  outfile-6  outfile-8
outfile-1  outfile-11  outfile-13  outfile-3  outfile-5  outfile-7  outfile-9

样品:

asux:~/proj/mini/forum > cat outfile-0
2 3 4  
5 6 7 8
9 10 11
asux:~/proj/mini/forum > cat outfile-1
1 3 4  
5 6 7 8
9 10 11

答案 3 :(得分:0)

使用perl的一种方式:

file.txt的内容:

1 2 3 4
5 6 7 8
9 10 11 12 13

script.pl的内容:

use warnings;
use strict;

## Read all input to a scalar variable as a single string.
my $str;
{
        local $/ = undef;
        $str = <>;
}

## Loop for each number found.
while ( $str =~ m/(\d+)(?:\h*)?/g ) {

        ## Open file for writing. The name of the file will be
        ## the number matched in previous regexp.
        open my $fh, q[>], ($1 . q[.txt]) or
                die qq[Couldn't create file $1.txt\n];

        ## Print everything prior to matched string plus everything
        ## after matched string.
        printf $fh qq[%s%s], $`, $';

        ## Close file.
        close $fh;
}

像以下一样运行:

perl script.pl file.txt

显示已创建的文件:

ls [0-9]*.txt

输出:

10.txt  11.txt  12.txt  13.txt  1.txt  2.txt  3.txt  4.txt  5.txt  6.txt  7.txt  8.txt  9.txt

显示其中一个的内容:

cat 9.txt

输出:

1 2 3 4
5 6 7 8
10 11 12 13