Wie überspringe ich eine Datei in sed, wenn sie reguläre Ausdrücke enthält?

Wie überspringe ich eine Datei in sed, wenn sie reguläre Ausdrücke enthält?

Ich verwende derzeit folgendesvereinfachter BefehlZunachstehende Leerzeichen entfernenUndfüge am Ende der Datei eine neue Zeile hinzuwo benötigt:

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

Wie Sie schnell sehen werden, gibt es dabei zwei Probleme: Es wird sich ändernBinärdateienund es wird eine neue Zeile am Ende der Dateien hinzugefügt mit␍␊ Zeilentrennzeichen. Diese Änderungen können beim Commiten git guioder Ähnlichem leicht rückgängig gemacht oder übersprungen werden, aber ich möchte die Anzahl der Rückgängigmachungen minimieren*. Zu diesem Zweck:

Gibt es eine Möglichkeit, das zu überspringen?ganzDatei, wennbeliebigZeile entspricht einem regulären Ausdruck in sed?

* Mir ist bewusst, dass es Binärdateien ohne ␀-Zeichen geben könnte und dass es Dateien mit absichtlich gemischten Zeilenumbrüchen oder ␀s geben könnte. Aber ich suche nach einer Lösung, die das minimale menschliche Eingreifen erfordert. IchkönnteEs wäre denkbar, alle Dateierweiterungen aufzulisten, die ich bearbeiten möchte, aber es wäre eine sehr lange Liste, die ständig überprüft werden müsste, und aufgrund von Namenskonflikten wäre es immer noch möglich, dass Binärdateien durchrutschen.

KompliziertProblemumgehung:

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)

Antwort1

Wenn Sie gitder Ansicht von vertrauen, was eine Binärdatei ist und was nicht, können Sie git grepeine Liste nichtbinärer Dateien abrufen. Angenommen, es t.cpphandelt sich um eine Textdatei und lseine Binärdatei, sind beide eingecheckt:

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

Die -IOption bedeutet:

-I
Passen Sie das Muster nicht in Binärdateien an.

So kombinieren Sie das mit Ihrem sedAusdruck:

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

( -z/ xargs -0als Hilfe bei seltsamen Dateinamen.)

Weitere nützliche Optionen finden Sie auf der git grepManpage. --no-indexDiese --cachedkönnten hilfreich sein, je nachdem, welchen Dateisatz Sie genau bearbeiten möchten.

Antwort2

Gibt es eine Möglichkeit, die gesamte Datei zu überspringen, wenn eine beliebige Zeile mit einem regulären Ausdruck in sed übereinstimmt?

Ja da ist.

# 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

Antwort3

Hier ist ein Perl-Skript, das seine Argumente (die Dateinamen sein müssen) durchläuft und an jede Datei, die nicht mit einer neuen Zeile endet, eine neue Zeile anfügt. Dateien, die ein Nullbyte enthalten, werden übersprungen. Dateien, die bereits mit einer neuen Zeile enden, bleiben unverändert. An Dateien, die ein CR enthalten, wird CRLF angehängt, an andere nur LF. Ungetestet.

#!/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;
    }
}

verwandte Informationen