¿Cómo omitir el archivo en sed si contiene expresiones regulares?

¿Cómo omitir el archivo en sed si contiene expresiones regulares?

Actualmente uso lo siguientecomando simplificadoaeliminar espacios en blanco al finalyagregar una nueva línea al final del archivodonde sea necesario:

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

Como verá rápidamente, esto tiene dos problemas: cambiaráarchivos binariosy agregará una nueva línea al final de los archivos con␍␊ separadores de línea. Estas modificaciones son fáciles de deshacer u omitir cuando se confirman git guio similares, pero me gustaría minimizar* la cantidad de reversiones. Con ese fin:

¿Hay alguna manera de saltarse elenteropresentar sicualquier¿La línea coincide con una expresión regular en sed?

* Soy consciente de que puede haber archivos binarios sin caracteres ␀ y podría haber archivos con nuevas líneas o ␀ mezcladas deliberadamente. Pero estoy buscando la solución que requiera la mínima intervención humana. IpodríaPosiblemente enumere todas las extensiones de archivos con las que me gustaría operar, pero sería una lista muy larga que tendría que revisarse constantemente y, debido a conflictos de nombres, aún sería posible que los archivos binarios se escapen.

Complicadosolución alterna:

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)

Respuesta1

Si confía giten el punto de vista de qué es un archivo binario o no, puede utilizarlo git greppara obtener una lista de archivos no binarios. Suponiendo que t.cppes un archivo de texto y lsun binario, ambos registrados:

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

La -Iopción significa:

-I
No coincida con el patrón en archivos binarios.

Para combinar eso con tu sedexpresión:

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

( -z/ xargs -0para ayudar con nombres de archivos extraños).

Consulte la git greppágina de manual para ver otras opciones útiles, --no-indexo --cachedpodrían resultar útiles dependiendo exactamente del conjunto de archivos con los que desee operar.

Respuesta2

¿Hay alguna manera de omitir todo el archivo si alguna línea coincide con una expresión regular en sed?

Sí hay.

# 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

Respuesta3

Aquí hay un script Perl que itera sobre sus argumentos (que deben ser nombres de archivos) y agrega una nueva línea a cada archivo que no termina en una nueva línea. Se omiten los archivos que contienen un byte nulo. Los archivos que ya terminan en una nueva línea no se modifican. A los archivos que contienen un CR se les agrega CRLF, a otros solo se les agrega LF. No probado.

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

información relacionada