Como pular o arquivo no sed se ele contiver regex?

Como pular o arquivo no sed se ele contiver regex?

Atualmente eu uso o seguintecomando simplificadopararemover espaço em branco à direitaeadicione uma nova linha no final do arquivoonde necessário:

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

Como você verá rapidamente, isso tem dois problemas: vai mudararquivos bináriose adicionará uma nova linha ao final dos arquivos com␍␊ separadores de linha. Essas modificações são fáceis de desfazer ou pular ao confirmar git guiou algo semelhante, mas eu gostaria de minimizar* a quantidade de reversão. Para esse fim:

Existe uma maneira de pular otodoarquivo sequalquerlinha corresponde a uma regex em sed?

* Estou ciente de que pode haver arquivos binários sem caracteres ␀ e pode haver arquivos com novas linhas ou ␀s deliberadamente misturados. Mas procuro a solução que requer o mínimo de intervenção humana. EUpoderiapossivelmente listaria todas as extensões de arquivo nas quais eu gostaria de operar, mas seria uma lista muito longa que teria que ser revisada constantemente e, devido a conflitos de nomes, ainda seria possível que arquivos binários escapassem.

ComplicadoGambiarra:

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

Responder1

Se você confia gitno ponto de vista de sobre o que é um arquivo binário ou não, você pode usar git greppara obter uma lista de arquivos não binários. Supondo que t.cppseja um arquivo de texto e lsum binário, ambos foram verificados:

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

A -Iopção significa:

-I
Não corresponda ao padrão em arquivos binários.

Para combinar isso com sua sedexpressão:

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

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

Confira a git greppágina de manual para outras opções úteis - --no-indexou --cachedpode ajudar dependendo exatamente do conjunto de arquivos em que você deseja operar.

Responder2

Existe uma maneira de pular o arquivo inteiro se alguma linha corresponder a uma regex no 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

Responder3

Aqui está um script Perl que itera sobre seus argumentos (que devem ser nomes de arquivos) e anexa uma nova linha a cada arquivo que não termina em uma nova linha. Arquivos contendo um byte nulo são ignorados. Arquivos que já terminam em nova linha não são modificados. Arquivos que contêm um CR são anexados ao CRLF, outros recebem apenas 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 (/\0/) {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;
    }
}

informação relacionada