sexta-feira, 22 de fevereiro de 2008

Alterando Multiplos Arquivos (versão final?)

O site Dicas-L trouxe na ultima semana 2 formas de renomear multiplos arquivos (aqui e aqui)

#Forma 1
for o in $(ls -1 *.txt); do
mv $o $(echo $o | awk -F. '{print $1".htm"}');
done


#Forma 2
for i in `ls *.txt`; do
mv $i $(echo `basename $i .txt`.html)
done


São ambas formas interessantes, porém ao meu ver consomem muito recursos da maquina, sem falar que são um tanto... feios... (nada contra - o que importa é que funcione)

Vou utiliza-los como exemplo de como podemos tornar algo melhor e mais prático (se o tempo permitir).

Vamos dividir as paradas:

altera() { mv $1 $(echo `basename $1 .txt`.html) ; }

for i in `ls *.txt`; do
altera $i
done


Agora vamos tomar um cuidado: arquivos com espaço no nome

altera() { mv "$1" $(echo `basename "$1" .txt`.html) ; }

for i in `ls *.txt`; do
altera "${i}"
done


Bom, o for pode iterar sobre uma lista de argumentos. As mascaras de nome de arquivo são expandidos pelo shell durante a execução, logo...

for i in *.txt ; do
altera "${i}"
done


Agora, a rotina de alteração do nome do arquivo de destino é complicadissima, depende de um ou mais sub-processos. Isso poderia ser...

altera() { mv "$1" "${1%.txt}.html" ; }


Que, inserido no for...

for i in *.txt ; do
mv "${i}" "${i%.txt}.html"
done


Interessante, certo? Nenhum sub-processo, exceto o inumeros mv que serão executados. Existem outras formas de fazer a mesma coisa

ls *.txt | awk -F. -v OFS=. '{ O=$0; $NF="html" ;printf "\"%s\" \"%s\"\n",O,$0 }' | xargs -n 2 mv

ls *.txt | sed 's#^\(.\+\)\.[^.]\+$#"&" "\1.html"#g' | xargs -n 2 mv


(rodem as linhas acima sem o mv do xargs para entende-las -- é metaprogramação)

Agora... tudo isso é muito bonito mas... veja se o seu computador possui os comandos mmv ou rename (que facilitam Absurdamente a tarefa)

rename .txt .html *.txt

mmv "*.txt" "#1.html"


Simples, não?

Tudo depende do tempo que temos e das nossas necessidades. Mesmo que o rename/mmv sejam uteis, pode ser que a forma com awk / sed valha mais a pena pois o ls pode ser substituido por um find (ja pensou nisso?)

3 comentários:

Leonardo Bernardes disse...

Tiago,

Você parece o sujeito certo pra esclarecer um dúvida:

Se eu quiser filtrar uma string do seguinte tipo

7718 0.0 0.0 1756 476 ? Ss 17:59 0:00 /bin/sh -c pidgin

Como AWK eu posso fazer através de algo como "awk '{print $1,$2,$10}'"

Mas se eu quiser, por exemplo, filtrar o $1, o $2 e todos OS DEMAIS após o $7 sem saber o número total de termos, há alguma variável pra isso? Não sei se me fiz entender, mas essa dúvida me persegue desde que lembrei que quando eu brincava editando scripts no mIRC, uma simples $7- realizava essa função

Desculpe o abuso, abraços

Tiago Peczenyj disse...

Ola Leonardo.

Infelizmente o gawk não tem o conceito de range. Quando vc utiliza o operador $X vc esta pegando o X-ésimo campo daquele registro. Vc tem uma variavel que é setada a cada registro que é NF, o numero de registros, que permite que vc leia o ultimo registro de forma simples

print $1,$NF

Agora... tem uma sacanagem que vc pode fazer: $0 é o registro inteiro, porém vc pode "queimar" alguns registros intermediarios

no seu caso, se vc fizesse

$3=$4=$5=$6=""
print $0

só sobraria o que vc quer.

Uma forma, menos agressiva, seria tentar pegar um padrão via expressões regulares.

Leonardo Bernardes disse...

Já conhecia o NF, depois de conhecê-lo queimei um pouco a cabeça tentando elaborar um método pra fazer essa passagem do 1 ao NF. Pensei em qualquer coisa semelhante ao while.. mas não veio nada.

Na verdade, Tiago, sou um belo amador, brinco de fazer scripts como terapia.. terapia que eu realizava aos montes na época de mIRC.

Por conta mesmo do meu amadorismo, não consegui entender seu exemplo de como queimar os registros intermediários. Mas não esquente com isso, se não há aquela variável mágica que eu usava no mIRC, é melhor que eu me contente com minhas limitações. Em todo caso, fico ligado nas suas dicas da seção shell.