
Tenho alguns arquivos Perl que preciso modificar por meio de uma simples sed
chamada. Basicamente, tenho que remover a primeira linha de todos eles. Atualmente, consegui fazer isso:
find <PATH> -type f -name "*.pl" -exec sed -i '1d' {} \;
No entanto, estou usando esta linha em um script e gostaria que fosse um pouco mais... falador. Assim, resolvi ecoar um contador (em tempo real), que mostraria quantos arquivos foram processados até o momento.
Eu sei que a contagem de arquivos Perl pode ser recuperada através
PERL_FILE_COUNT=$(find <PATH> -name "*.pl" | wc -l)
Atualmente eu tenho isso
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
}
Agora, o que eu gostaria como saída seria algo assim:
"Removing first line of Perl files ... 1/209209"
E o valor deverá ser atualizado automaticamente. Mas não vejo como incrementar a count
variável com minha instrução find/exec. Basicamente, cada vez que sed
terminar de trabalhar em um arquivo, ele deverá incrementar a count
variável.
Responder1
Se você tiver o bash 4, considere usar o globstar. Dá a você 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 solução funcionará em arquivos com caracteres malucos em seus nomes e evitará um subshell.
Mas se o bash 4 não for uma opção, você pode recriar esta solução 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 {} +
No entanto, isso está sujeito ao ARG_MAX do seu sistema (ao contrário do acima), portanto, se o número de arquivos for muito grande, você ainda poderá acabar com várias execuções em subconjuntos de arquivos.
Responder2
Que tal agora?
#!/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"
É mais seguro usar sed -i~
aqui. Sed salva o arquivo antigo como file~
.
Responder3
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 ""}'