Убрать одну строку из множества файлов

Я пытаюсь удалить строку из многих текстовых файлов на одном из наших серверов. Строка одинакова для всех этих файлов, и я могу запустить:

grep -r -l 'string'  

чтобы получить список файлов, но я застрял на том, как получить файлы, отредактированные и записанные в их исходных местах снова. Звучит как работа для sed, но не уверен, как справиться с выводом.

5 ответов

Решение

Вот мой сценарий для такого рода вещей, который я называю remove_line:

#!/usr/bin/perl

use IO::Handle;

my $pat = shift(@ARGV) or
        die("Usage: $0 pattern files\n");
$pat = qr/$pat/;
die("Usage $0 pattern files\n")
        unless @ARGV;

foreach my $file (@ARGV) {
        my $io = new IO::Handle;
        open($io, $file) or
                die("Cannot read $file: $!\n");
        my @file = <$io>;
        close($io);
        foreach my $line (@file) {
                if($line =~ /$pat/) {
                        $line = '';
                        $found = 1;
                        last;
                }
        }
        if($found) {
                open($io, ">$file") or
                        die("Cannot write $file: $!\n");
                print $io @file;
                close($io);
        }
}

Так вы делаете remove_line 'string' файлы в вашем списке.

Преимущества делать это по сравнению с использованием sed Вы не должны беспокоиться о платформо-зависимом поведении sed -i и вы можете использовать регулярное выражение Perl для соответствующего шаблона.

find -type f -print0 | xargs -0 -n 1 sed -i /string/d сделает свое дело, обрабатывая пробелы в именах файлов и произвольно вложенных фруктах, так как очевидно, что люди не способны расширяться * самостоятельно.

Тьфу. Я вовсе не волшебник оболочки, но я бы посмотрел на канал для xargs, а затем на sed, чтобы удалить строку с рассматриваемой строкой.

Little bit of Google perusal makes me think that this might make Bob your stepuncle - close enough to get there anyway.

grep -r -l 'string'  | xargs sed '/string/d' 

Хмммммм, это perl one-liner, благодаря прекрасному флагу -i для фильтрации входных файлов на месте!!

 perl -ni.bak -e 'печатать, если /pattern.to.remove/' file1 file2 ... 

В контексте...

% echo -e 'foo\ngoo\nboo' >test
% perl -ni.bak -e 'print unless /goo/' test
% diff test*
--- test 2010-01-06 05:09:13.503334739 -0800
+++ test.bak 2010-01-06 05:08:28.313583066 -0800
@@ -1,2 +1,3 @@
 foo
+goo
 boo

Вот краткий справочник об использованном заклинании Perl...

% perl --help
Usage: perl [switches] [--] [programfile] [arguments]
  -e program        one line of program (several -e's allowed, omit programfile)
  -i[extension]     edit <> files in place (makes backup if extension supplied)
  -n                assume "while (<>) { ... }" loop around program

и для дополнительного кредита, вы можете использовать touch -r file.bak file скопировать старую метку времени в новый файл. однако, иноды будут отличаться, и странные вещи могут произойти, если у вас есть жесткие ссылки в миксе... проверьте документы, если вы заинтересованы в том, чтобы скрыть свои треки... Хмммм, что вы снова использовали?

Не забывайте о опции -v в grep, которая меняет смысл

grep -v -r -l 'string' 

Справочная страница Frok fom grep:

-v, --invert-match
Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX.)

Затем вы можете передать это в команду поиска, подобную этой

find -name -exec grep -v -r -l 'string' {} \;

И это приближается к тому, что вы хотите... но, конечно, вам нужно записать результат обратно в исходный файл...

Другие вопросы по тегам