Bash regex para renomear conjunto de arquivos

Bash regex para renomear conjunto de arquivos

Tenho que renomear um conjunto de arquivos, usando o renamecomando (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 .txtextensã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

$datahoratemdata 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 prefixparte, e o último elemento faz other.txtparte 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 arrayvariante grava apenas uma vez no disco.

¿O que você acha dessas duas soluções?...

Obrigado novamente.

Responder1

A menos que você renamepossa 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 DATETIMEvariá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ê renamesuportar vários comandos, basta concatená-los:

rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt

informação relacionada