Retira uma única linha de muitos arquivos

3

Estou tentando remover uma string de muitos arquivos de texto em um de nossos servidores. A string é idêntica em todos esses arquivos e eu posso executar:

grep -r -l 'string'  

para obter a lista de arquivos, mas estou preso sobre como obter os arquivos editados e gravados em seus locais originais novamente. Soa como um trabalho para sed, mas não sabe como lidar com a saída.

    
por Manny T 05.01.2010 / 23:07

5 respostas

3

Aqui está o meu script para esse tipo de coisa, que eu chamo de 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);
        }
}

Então você faz remove_line 'string' os arquivos em sua lista.

As vantagens de se fazer isso usando sed é que você não precisa se preocupar com o comportamento dependente da plataforma de sed -i e você pode usar o regex Perl para o padrão correspondente.

    
por 05.01.2010 / 23:13
5

find -type f -print0 | xargs -0 -n 1 sed -i /string/d irá fazer o truque, manipulação de espaços em nomes de arquivos e frufru arbitrariamente aninhados, pois, aparentemente, as pessoas não são capazes de expandir * por conta própria.

    
por 05.01.2010 / 23:11
1

Ugh. Eu não sou um assistente de shell, mas eu olhei para um pipe para xargs e depois sed para remover a linha com a string em questão.

Um pouco da leitura do Google me faz pensar que isso pode fazer de Bob seu próximo passo - o suficiente para chegar lá de qualquer maneira.

grep -r -l 'string'  | xargs sed '/string/d' 
    
por 05.01.2010 / 23:14
0

Ummmmmm, este é um perl one-liner , graças à adorável -i sinalização para filtros in-loco de arquivos de entrada !!

   perl -ni.bak -e 'print unless /pattern.to.remove/' file1 file2 ...

No contexto ...

% 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

aqui está a referência rápida aparada no encantamento perl usado ...

% 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

e para crédito extra, você pode usar touch -r file.bak file para copiar o timestamp antigo para o novo arquivo. Porém, os inodes serão diferentes, e coisas estranhas podem acontecer se você tiver hard links no mix ... verifique os documentos se você está motivado para cobrir suas faixas ... Hmmmmm, o que foi sua aplicação novamente?

    
por 06.01.2010 / 14:32
-1

Não se esqueça da opção -v no grep que inverte o sentido

grep -v -r -l 'string' 

Página do manual do Frok fom grep:

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

Você pode então passar isso para o comando find similar a este

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

E isso está chegando perto do que você quer ... mas é claro que você precisará escrever o resultado de volta para o arquivo original ...

    
por 06.01.2010 / 00:13