
Tengo algunos archivos Perl que debo modificar mediante una simple sed
llamada. Básicamente, tengo que eliminar la primera línea de todos ellos. Actualmente, logré hacer esto:
find <PATH> -type f -name "*.pl" -exec sed -i '1d' {} \;
Sin embargo, estoy usando esta línea en un guión y me gustaría que fuera un poco más... hablador. Por lo tanto, decidí hacer eco de un contador (en tiempo real), que mostraría cuántos archivos se han procesado hasta el momento.
Sé que el recuento de archivos Perl se puede recuperar a través de
PERL_FILE_COUNT=$(find <PATH> -name "*.pl" | wc -l)
Actualmente tengo este
remove_first_line()
{
count=0
echo -ne "Removing first line of Perl files ..."
find <PATH> -type f -name "*.pl" -exec sed -i '1d' {} \; >/dev/null 2>&1
if [ $? -eq 0 ]
then echo "OK"
then echo "FAILED"
fi
}
Ahora, lo que me gustaría como resultado sería algo como esto:
"Removing first line of Perl files ... 1/209209"
Y el valor debería actualizarse automáticamente. Pero no veo cómo incrementar la count
variable con mi declaración find/exec. Básicamente, cada vez que sed
termine de trabajar en un archivo, debería incrementar la count
variable.
Respuesta1
Si tienes bash 4, considera usar globstar en su lugar. Te da globbing recursivo.
shopt -s globstar
perlz=( **/*.pl ) # */ Hack to fix syntax highlighting
totes="${#perlz[@]}"
i=0
for file in "${perlz[@]}"; do
printf 'Removing first line of Perl files … %d/%d\r' $((++i)) $totes
ed -s "$file" <<< $'1d\nw' # You can use `sed` if you want to, but ed is an actual file editor
done
echo # print a final newline
Esta solución funcionará en archivos con caracteres locos en sus nombres y evita una subcapa.
Pero si bash 4 no es una opción, puedes recrear esta solución usando find -exec +
:
find . -name '*.pl' -exec bash -c 'totes=$#
i=0
for file; do
printf "Removing first line of Perl files … %d/%d\r" $((++i)) $totes
ed -s "$file" <<< $'\''1d\nw'\'' # Avoid these leaning toothpicks by putting this
# script in a file.
done
echo # print a final newline
' bash {} +
Sin embargo, esto está sujeto al ARG_MAX de su sistema (a diferencia de lo anterior), por lo que si la cantidad de archivos es muy grande, aún podría terminar con múltiples ejecuciones sobre subconjuntos de archivos.
Respuesta2
¿Qué tal esto?
#!/bin/bash
failed=0
find . -type f -name "*.pl" | while read file; do
if [ -e "$file" ] && [ -r "$file" ]; then
sed -i~ "1d" "$file"
if [ $? != 0 ]; then
echo "sed returns $? on ($file)"
(( failed++ ))
fi
else
echo "warning ($file) not exists, or not readable"
(( failed++ ))
fi
done
echo "failed execution: $failed"
Es más seguro usarlo sed -i~
aquí. Sed guarda el archivo antiguo como file~
.
Respuesta3
GNUly:
find . -type f -name '*.pl' -size +0c -print0 > list &&
count=$(grep -cz . < list) &&
stdbuf -oL xargs < list -r0 sed -i -e '1{w /dev/stdout' -e 'd;}' |
awk -v c="$count" '{printf "processed file %d/%d\r", NR, c}
END{print ""}'