Resultado da contagem em uma instrução find / exec

2

Eu tenho alguns arquivos Perl que eu tenho que modificar através de uma simples chamada sed . 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 ... falante. Assim, decidi ecoar um contador (em tempo real), que mostraria quantos arquivos foram processados até agora.

Eu sei que a contagem de arquivos Perl pode ser recuperada por meio de

PERL_FILE_COUNT=$(find <PATH> -name "*.pl" | wc -l)

Atualmente, 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 deve ser atualizado automaticamente. Mas não vejo como incrementar a variável count com minha instrução find / exec. Basicamente, cada vez que sed terminasse de trabalhar em um arquivo, ele deveria incrementar a variável count .

    
por Yggdrasil_ 27.03.2014 / 15:19

3 respostas

3

Se você tiver o bash 4, considere usar globstar. Isso lhe dá globalização recursiva.

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 funciona em arquivos com caracteres malucos em seus nomes e evita um subnível.

Mas se o bash 4 não for uma opção, você poderá recriar essa 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 a 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 de subconjuntos dos arquivos.

    
por 27.03.2014 / 20:11
1

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 ""}'
    
por 27.03.2014 / 22:22
0

Que tal isso?

#!/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~ .

    
por 27.03.2014 / 20:11

Tags