Como pular o arquivo em sed se contiver regex?

4

Atualmente, uso o seguinte comando simplificado para remover o espaço em branco e adicione uma nova linha no final do arquivo onde for necessário:

find . -type f -exec sed -i -e 's/[ \t]\+\(\r\?\)$//;$a\' {} \+

Como você verá rapidamente, isso tem dois problemas: ele alterará arquivos binários e adicionará uma nova linha ao final dos arquivos com ␍␊ separadores de linha . Essas modificações são fáceis de desfazer ou pular ao se comprometer em git gui ou algo parecido, mas eu gostaria de minimizar * a quantidade de reversão. Para esse fim:

Existe uma maneira de pular o arquivo todo se a linha qualquer corresponder a uma regex em sed ?

* Estou ciente de que pode haver arquivos binários sem ␀ caracteres, e pode haver arquivos com novas linhas deliberadamente mistas ou ␀s. Mas estou procurando a solução que requer a mínima intervenção humana. Eu poderia conceitivelmente listar todas as extensões de arquivo que eu gostaria de operar, mas seria uma lista muito longa que teria que ser constantemente revisada, e por causa dos confrontos de nome ainda seria possível que arquivos binários passam.

Solução complicada :

while IFS= read -r -d '' -u 9
do
    if [[ "$(file -bs --mime-type -- "$REPLY")" = text/* ]]
    then
        sed -i -e 's/[ \t]\+\(\r\?\)$//;$a\' -- "$REPLY"
    else
        echo "Skipping $REPLY" >&2
    fi
done 9< <(find . -type f -print0)
    
por l0b0 11.04.2012 / 14:53

3 respostas

2

Se você confia no ponto de vista de git sobre o que é um arquivo binário ou não, você pode usar git grep para obter uma lista de arquivos não binários. Assumindo que t.cpp é um arquivo de texto, e ls é um binário, ambos checados em:

$ ls
t.cpp ls
$ git grep -I --name-only -e ''
t.cpp

A opção -I significa:

-I
Don't match the pattern in binary files.

Para combinar isso com sua expressão sed :

$ git grep -I --name-only -z -e '' | \
       xargs -0 sed -i.bk -e 's/[ \t]\+\(\r\?\)$//;$a\'

( -z / xargs -0 para ajudar com nomes estranhos de arquivos.)

Confira a página git grep man para outras opções úteis - --no-index ou --cached pode ajudar dependendo de exatamente qual conjunto de arquivos você deseja operar.

    
por 11.04.2012 / 15:54
2

Is there a way to skip the whole file if any line matches a regex in sed?

Sim, existe.

# test case for skipping file if a sed regex match succeeds

echo 'Hello, world!' > hello_world.txt
cat hello_world.txt
ls -li hello_world.txt

sed -i -e '/.*Hello.*/{q;}; s/world/WORLD/g' hello_world.txt # skips file
sed -i -e '/.*HeLLo.*/{q;}; s/world/WORLD/g' hello_world.txt
    
por 21.02.2014 / 14:48
1

Aqui está um script Perl que itera sobre seus argumentos (que devem ser nomes de arquivos) e acrescenta uma nova linha a cada arquivo que não termina em uma nova linha. Arquivos contendo um byte nulo são ignorados. Os arquivos que já terminam em uma nova linha não são modificados. Os arquivos que contêm um CR obtêm o CRLF anexado, outros recebem apenas o LF. Não testado.

#!/usr/bin/env perl
foreach my $f (@ARGV) {
    open F, "<", $f or die;
    my $last = undef;
    my $cr = 0;
    while (<>) {if (/
#!/usr/bin/env perl
foreach my $f (@ARGV) {
    open F, "<", $f or die;
    my $last = undef;
    my $cr = 0;
    while (<>) {if (/%pre%/) {undef $last; break} $last = $_; ++$cr if /\r$/}
    close F;
    if (defined $last && $last !~ /\n\Z/) {
        open F, ">>", $f or die;
        print($cr ? "\r\n" : "\n");
        close F or die;
    }
}
/) {undef $last; break} $last = $_; ++$cr if /\r$/} close F; if (defined $last && $last !~ /\n\Z/) { open F, ">>", $f or die; print($cr ? "\r\n" : "\n"); close F or die; } }
    
por 12.04.2012 / 00:45