удалить строки из нескольких файлов с помощью gawk/awk/sed

У меня есть два набора текстовых файлов. Первый набор находится в папке AA. Второй комплект находится в папке BB. Содержимое файла ff.txt из первого набора (папка AA) показано ниже.

Name        number     marks
john            1         60
maria           2         54
samuel          3         62
ben             4         63

Я хотел бы напечатать второй столбец (число) из этого файла, если отметки> 60. Выход будет 3,4. Далее читаем файл ff.txt в папке ББ и удаляем строки содержащие цифры 3,4.

файлы в папке ББ выглядят так. второй столбец - число.

 marks       1      11.824  24.015  41.220  1.00 13.65
 marks       1      13.058  24.521  40.718  1.00 11.82
 marks       3      12.120  13.472  46.317  1.00 10.62
 marks       4      10.343  24.731  47.771  1.00  8.18

Я использовал следующий код. Этот код отлично работает для одного файла.

gawk 'BEGIN {getline} $3>60{print $2}' AA/ff.txt | while read number; do gawk -v number=$number '$2 != number' BB/ff.txt > /tmp/ff.txt; mv /tmp/ff.txt BB/ff.txt; done

Но когда я запускаю этот код с несколькими файлами, я получаю сообщение об ошибке.

gawk 'BEGIN {getline} $3>60{print $2}' AA/*.txt | while read number; do gawk -v number=$number '$2 != number' BB/*.txt > /tmp/*.txt; mv /tmp/*.txt BB/*.txt; done

error:-
mv: target `BB/kk.txt' is not a directory

Я задал этот вопрос два дня назад. Пожалуйста, помогите мне решить эту ошибку.


person rebca    schedule 13.05.2012    source источник


Ответы (3)


Это создает индекс всех файлов в папке AA и проверяет все файлы в папке BB:

cat AA/*.txt | awk 'FNR==NR { if ($3 > 60) array[$2]; next } !($2 in array)' - BB/*.txt

Это сравнивает два отдельных файла, предполагая, что они имеют одинаковое имя в папках AA и BB:

ls AA/*.txt | sed "s%AA/\(.*\)%awk 'FNR==NR { if (\$3 > 60) array[\$2]; next } !(\$2 in array)' & BB/\1 %" | sh

ХТН

ИЗМЕНИТЬ

Это должно помочь :-)

ls AA/*.txt | sed "s%AA/\(.*\)%awk 'FNR==NR { if (\$3 > 60) array[\$2]; next } !(\$2 in array)' & BB/\1 > \1_tmp \&\& mv \1_tmp BB/\1 %" | sh

person Steve    schedule 13.05.2012
comment
Спасибо за ваш ответ. Я хотел бы удалить строки из файлов в папке BB. Файлы имеют одинаковое имя в AA и BB. Как я могу изменить ваш код? - person rebca; 13.05.2012
comment
К сожалению, awk не поддерживает встроенное редактирование. Мое редактирование создает временный файл, а затем заменяет его. - person Steve; 13.05.2012

> /tmp/*.txt и mv /tmp/*.txt BB/*.txt неверны.


Для одного файла

awk 'NR>1 && $3>60{print $2}' AA/ff.txt > idx.txt

awk 'NR==FNR{a[$0]; next}; !($2 in a)' idx.txt BB/ff.txt

Для нескольких файлов

awk 'FNR>1 && $3>60{print $2}' AA/*.txt >idx.txt

cat BB/*.txt | awk 'NR==FNR{a[$0]; next}; !($2 in a)' idx.txt -
person kev    schedule 13.05.2012
comment
Спасибо за ваш ответ. Ваш код печатает только числа вместо удаления строк. - person rebca; 14.05.2012
comment
idx.txt это временный файл. Вы должны запустить две команды одну за другой. Вторая команда напечатает то, что вы хотите. - person kev; 15.05.2012

Одно perl решение:

use warnings;
use strict;
use File::Spec;

## Hash to save data to delete from files of BB folder.
## key -> file name.
## value -> string with numbers of second column. They will be
## joined separated with '-...-', like: -2--3--1-. And it will be easier to
## search for them using a regexp.
my %delete;

## Check arguments:
## 1.- They are two.
## 2.- Both are directories.
## 3.- Both have same number of regular files and with identical names.
die qq[Usage: perl $0 <dir_AA> <dir_BB>\n] if
        @ARGV != 2 ||
        grep { ! -d } @ARGV;

{
        my %h;
        for ( glob join q[ ], map { qq[$_/*] } @ARGV ) {
                next unless -f;
                my $file = ( File::Spec->splitpath( $_ ) )[2] or next;
                $h{ $file }++;
        }

        for ( values %h ) {
                if ( $_ != 2 ) {
                        die qq[Different files in both directories\n];
                }
        }
}

## Get files from dir 'AA'. Process them, print to output lines which 
## matches condition and save the information in the %delete hash.
for my $file ( glob( shift . qq[/*] ) ) {
        open my $fh, q[<], $file or do { warn qq[Couldn't open file $file\n]; next };
        $file = ( File::Spec->splitpath( $file ) )[2] or do { 
                warn qq[Couldn't get file name from path\n]; next };
        while ( <$fh> ) {
                next if $. == 1;
                chomp;
                my @f = split;
                next unless @f >= 3;
                if ( $f[ $#f ] > 60 ) {
                        $delete{ $file } .= qq/-$f[1]-/;
                        printf qq[%s\n], $_;
                }
        }
}

## Process files found in dir 'BB'. For each line, print it if not found in
## file from dir 'AA'.
{
        @ARGV  = glob( shift . qq[/*] );
        $^I = q[.bak];
        while ( <> ) {

                ## Sanity check. Shouldn't occur.
                my $filename = ( File::Spec->splitpath( $ARGV ) )[2];
                if ( ! exists $delete{ $filename } ) {
                        close ARGV;
                        next;
                }

                chomp;
                my @f = split;
                if ( $delete{ $filename } =~ m/-$f[1]-/ ) {
                        next;
                }

                printf qq[%s\n], $_;
        }
}

exit 0;

тест:

Предполагая следующее дерево файлов. Команда:

ls -R1

Выход:

.:
AA
BB
script.pl

./AA:
ff.txt
gg.txt

./BB:
ff.txt
gg.txt

И далее содержимое файлов. Команда:

head AA/*

Выход:

==> AA/ff.txt <==
Name        number     marks
john            1         60
maria           2         54
samuel          3         62
ben             4         63
==> AA/gg.txt <==
Name        number     marks
john            1         70
maria           2         54
samuel          3         42
ben             4         33

Команда:

head BB/*

Выход:

==> BB/ff.txt <==
 marks       1      11.824  24.015  41.220  1.00 13.65
 marks       1      13.058  24.521  40.718  1.00 11.82
 marks       3      12.120  13.472  46.317  1.00 10.62
 marks       4      10.343  24.731  47.771  1.00  8.18
==> BB/gg.txt <==
 marks       1      11.824  24.015  41.220  1.00 13.65
 marks       2      13.058  24.521  40.718  1.00 11.82
 marks       3      12.120  13.472  46.317  1.00 10.62
 marks       4      10.343  24.731  47.771  1.00  8.18

Запустите скрипт следующим образом:

perl script.pl AA/ BB

Со следующим выводом на экран:

samuel          3         62
ben             4         63
john            1         70

И файлы каталога BB изменены следующим образом:

head BB/*

Выход:

==> BB/ff.txt <==
 marks       1      11.824  24.015  41.220  1.00 13.65
 marks       1      13.058  24.521  40.718  1.00 11.82

==> BB/gg.txt <==
 marks       2      13.058  24.521  40.718  1.00 11.82
 marks       3      12.120  13.472  46.317  1.00 10.62
 marks       4      10.343  24.731  47.771  1.00  8.18

Итак, из ff.txt были удалены строки с номерами 3 и 4, а строки с номерами 1 из gg.txt, которые все были больше 60 в последнем столбце. Я думаю, это то, чего вы хотели добиться. Надеюсь поможет, хотя и не awk.

person Birei    schedule 13.05.2012