
Tenho que renomear um conjunto de arquivos, usando o rename
comando (com uma expressão regular). Depois de algumas tentativas, não consigo encontrar uma expressão que obtenha o resultado esperado.
Eu tenho um padrão de arquivo como esse:
prefixo_algum_nome_outro.txt
Todos os arquivos começam com a prefix_
string " " e terminam com " _other.txt
", e oalgum_nomeparte pode consistir em várias palavras (alfanuméricas) separadas por sublinhados. Então é possível ter:
prefix_one_name_other.txt
prefix_this_is_my_name_1_this1_other.txt
Preciso renomear os nomes dos arquivos como estes:
outro_um-nome_data hora outro_este-é-meu-nome-1-este1_data hora
Em outras palavras:
- Precisa excluir "
prefix
" (deixando o sublinhado) - "
other
" token vai para o início do nome do arquivo - Emalgum_nome, converta sublinhado (_) em traço (-)
- O sublinhado no final do nome do arquivo (apósalgum_nome) deve permanecer
- Precisa excluir a
.txt
extensão, substituída pordata hora.
O que eu tentei:
rename 's/fw_([a-z]+)_(\d)_(\w+\d)_(\w+)\.txt/$4_$1-$2-$3_'$datahora'/' *.txt
$datahora
temdata horavalor (testado). Isso funciona como esperado com
prefix_name_1_gnt1_other.txt
mas não com
prefix_other_name_2_gnt2_other.txt
Onde foi que eu errei? De que outra forma eu poderia conseguir isso?
Fiquei pensando, pois por enquanto não consigo encontrar um regex que funcione para todos os nomes de arquivos que tenho. Eu sei que o primeiro elemento da string é sempre a prefix
parte, e o último elemento faz other.txt
parte da string. Portanto, é possível dividir a string em um array e obter os itens necessários para construir o novo nome. Na verdade, algo assim.
datahora="20140718-080000"
arrfiles=( *.txt )
for curfile in ${arrfiles[*]}
do
arrparts=( ${curfile//_/ } )
numitems=${#arrparts[*]}
newname=""
for (( c=1; c<numitems-1; c++ ))
do
newname+="${arrparts[c]}-"
done
newname=${newname%-}
arrparts[numitems-1]=${arrparts[numitems-1]/.txt/}
newname="${arrparts[numitems-1]}_${newname}_$datahora"
echo "$curfile pasa a $newname"
mv ${curfile} ${newname}
done
Depois de fazer isso, tentei novamente a sugestão do @peterph e, finalmente, concluí algumas combinações de renomeação de regex. Pense algo assim:
rename 's/_/-/g' *.txt
rename 's/^fw-(.*)-([^-]*)(\.txt)/$2.$1$3/' *.txt
rename 's/(\w+)\.(.*)(\.txt)/$1_$2_'$datahora'/' *.txt
Não tenho certeza de qual é a melhor abordagem. Na minha opinião, a variante regex parece mais elegante, mas preciso de três operações de renomeação (acesso três vezes ao disco) para fazer o trabalho, enquanto a array
variante grava apenas uma vez no disco.
¿O que você acha dessas duas soluções?...
Obrigado novamente.
Responder1
A menos que você rename
possa aceitar vários comandos de substituiçãoea raiz do nome do arquivo ( some_name
) pode conter mais de um sublinhado, você deve fazer isso em duas etapas: a) substituir sublinhados por travessões eb) (re)mover pedaços nos nomes dos arquivos.
As expressões regulares que você procura podem ser, por exemplo:
rename 's/_/-/g' *.txt
rename 's/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt
O primeiro faz o sublinhado para as traduções dos traços, enquanto o último faz a troca de raiz e sufixo e anexa o conteúdo da DATETIME
variável de ambiente aos nomes. E omite o prefixo e a extensão, é claro.
A [^-]*
parte corresponde a qualquer string que não contenha um travessão. Caso o sufixo seja sempre o mesmo você pode colocá-lo literalmente como é o caso do prefixo (e vice-versa - se o prefixo puder variar, use ^[^-]*-
para combiná-lo como qualquer string que não contenha um traço localizado entre o início do arquivo nome e (portanto) o primeiro travessão).
Se você rename
suportar vários comandos, basta concatená-los:
rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt