Substituindo pontos no nome do arquivo por sublinhados, exceto a extensão

9

Alguém pode sugerir sobre como renomear o nome do arquivo:

head.body.date.txt

Para:

head_body_date.txt

Existe uma declaração de linha única para renomear no Unix ?

SG
fonte
9
Você acha que isso .tar.gzé uma extensão? Além disso, seja bem-vindo à U&L!
EKons
2
Você realmente quer dizer Unix em oposição ao Linux? Qual Unix?
terdon
1
A resposta óbvia é mv head.body.date.txt head_body_date.txt. Se você tiver outras restrições ou exemplos reais, atualize sua pergunta com estes. Como está agora, não está claro se a pergunta se refere a um arquivo com esse nome explícito, um único diretório em que todos os arquivos têm um formato específico ou se você deseja pesquisar na hierarquia de diretórios por nomes específicos e alterá-los.
Kusalananda

Respostas:

9

Itere sobre os nomes de arquivos e use a expansão Parameter para a conversão:

for f in *.*.*.txt; do i="${f%.txt}"; echo mv -i -- "$f" "${i//./_}.txt"; done

O padrão de expansão do parâmetro ${f//./_}substitui todos os .s por _s no nome do arquivo ( $f).

O procedimento acima será executado a seco, para permitir que a renomeação real ocorra, remova echo:

for f in *.*.*.txt; do i="${f%.txt}"; mv -i -- "$f" "${i//./_}.txt"; done

Se você deseja lidar com qualquer extensão, não apenas .txt:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     echo mv -i -- "$f" "${pre//./_}.${suf}"; done

Após verificar remover echopara ação real:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     mv -i -- "$f" "${pre//./_}.${suf}"; done

Genérico, para número arbitrário de pontos, pelo menos um:

for f in *.*; do pre="${f%.*}"; suf="${f##*.}"; \
                 mv -i -- "$f" "${pre//./_}.${suf}"; done
heemail
fonte
existe uma maneira geral se houver um número arbitrário de pontos?
Ciprian Tomoiagă
1
@CiprianTomoiaga Do:for f in *.*; do ...; done
heemayl
Eu posso sugerir que o *.*padrão seja reto no exemplo, já que agora o código parece funcionar apenas com um número fixo de pontos.
Ilkkachu 11/10
@ilkkachu Fair bastante, editado.
heemayl
Muito obrigado heemay, funcionou para mim. e alguma aprendizagem!
SG
4

com perlbaserename

$ rename -n 's/\.[^.]+$(*SKIP)(*F)|\./_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)
  • \.[^.]+$(*SKIP)(*F) pule esse padrão e procure correspondências alternativas
  • |\./_/gsubstitua tudo .por_

Ou, usando lookahead negativo

$ rename -n 's/\.(?![^.]+$)/_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)

Quando estiver tudo bem, remova a -nopção

Sundeep
fonte
3
rename 's/\.(?=[^.]*\.)/_/g' *.txt

Usa a substituição de regex para localizar tudo, exceto a última instância do .nome do arquivo (lookahead que não captura) e substituí-los por _. Generaliza *.*se quiser.

(esta versão específica do renameparece estar instalada via util-linux). Estou usando Ubuntu 12.04(sim, máquina severamente desatualizada).

Jonathan Carroll
fonte
2

bash:

old=head.body.date.txt oldb=${old%.*} olde=${old##*.} ; \
mv -- "$old" "${oldb//./_}.${olde}"
agc
fonte
(1) Você deve usar mv --para se proteger contra nomes de arquivos “antigos” que começam com -. (2) Você deve colocar $oldaspas ( "$old"), para se proteger contra nomes de arquivos “antigos” que contêm espaços ou caracteres especiais para o shell (como *e ?). (3) Sua resposta falha no caso um tanto patológico em que a extensão contém um sublinhado: head.body.date.t_xté renomeada para head_body_date_t.xt.
Scott
@ Scott, obrigado. O código agora é ajustado conforme os três itens mencionados.
AGC
1

Usando mv, sede revem uma linha:

mv "head.body.date.txt" "$(echo head.body.date.txt | rev | sed 's/\./_/2g' | rev)"

Se você deseja aplicá-lo em todos os txtarquivos no representante atual, o uso de globs parece complicado por causa de como mvfunciona, mas você pode fazer um forloop em uma linha:

for file in *.txt; do mv "$file" "$(echo $file | rev | sed 's/\./_/2g' | rev)"; done

Um pouco mais, mas você pode combinar vários padrões no seu loop!

Valentin B.
fonte
2
(1) Arrghhh! Não faça $(ls …); apenas faça for file in *.txt. (2) Você deve usar mais citações: mv "$file" "$(…)".
Scott
Obrigado Scott, devidamente anotado e editado! Eu ainda sou um jovem com casca, por favor, ser gentil;)
Valentin B.
Em retrospectiva, usando renamecomo vários outros sugeriram em suas respostas faz som mais simples embora ...
Valentin B.
-1
 mv head.body.date.txt head_body_date.txt

É tão simples quanto isso para um único arquivo de cada vez. Você não especifica se deseja que isso seja feito recursivamente em um diretório ou em todo o diretório, para que essa seja a solução mais conveniente e rápida.

coderodour
fonte