Как пропустить файл в sed, если он содержит регулярное выражение?

Как пропустить файл в sed, если он содержит регулярное выражение?

В настоящее время я использую следующееупрощенная командакудалить конечные пробелыидобавить новую строку в конец файлагде необходимо:

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

Как вы быстро увидите, здесь есть две проблемы: это изменитсядвоичные файлыи он добавит новую строку в конец файлов с␍␊ разделители строк. Эти изменения легко отменить или пропустить при фиксации git guiили чем-то подобном, но я хотел бы минимизировать* количество откатов. Для этого:

Есть ли способ пропуститьвесьфайл еслилюбойстрока соответствует регулярному выражению в sed?

* Я знаю, что могут быть двоичные файлы без символов ␀, и могут быть файлы с намеренно смешанными символами новой строки или ␀. Но я ищу решение, которое требует минимального человеческого вмешательства. Ямогможно было бы перечислить все расширения файлов, с которыми я хотел бы работать, но это был бы очень длинный список, который пришлось бы постоянно просматривать, и из-за конфликтов имен все равно были бы шансы, что в него проскользнут двоичные файлы.

Сложныйобходной путь:

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)

решение1

Если вы доверяете gitточке зрения на то, что является двоичным файлом, а что нет, вы можете использовать git grepдля получения списка недвоичных файлов. Предполагая, t.cppчто это текстовый файл, а lsэто двоичный файл, оба проверены в:

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

Опция -Iозначает:

-I
Не сопоставляйте шаблон в двоичных файлах.

Чтобы объединить это с вашим sedвыражением:

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

( -z/ xargs -0для помощи со странными именами файлов.)

Ознакомьтесь со git grepстраницей руководства для получения информации о других полезных опциях ( --no-indexили --cachedвозможной помощи в зависимости от того, с каким именно набором файлов вы хотите работать).

решение2

Есть ли способ пропустить весь файл, если какая-либо строка соответствует регулярному выражению в sed?

Да, есть.

# 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

решение3

Вот скрипт Perl, который перебирает свои аргументы (которые должны быть именами файлов) и добавляет новую строку к каждому файлу, который не заканчивается новой строкой. Файлы, содержащие нулевой байт, пропускаются. Файлы, которые уже заканчиваются новой строкой, не изменяются. Файлы, содержащие CR, получают CRLF, другие получают только LF. Не проверено.

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

Связанный контент