Mengganti titik dalam nama file dengan garis bawah kecuali ekstensi

9

Adakah yang bisa menyarankan cara mengubah nama nama file:

head.body.date.txt

Untuk:

head_body_date.txt

Apakah ada pernyataan baris tunggal untuk melakukan penggantian nama di Unix ?

SG
sumber
9
Apakah Anda menganggap itu .tar.gzekstensi? Juga, selamat datang di U&L!
EKons
2
Apakah Anda benar-benar mengartikan Unix sebagai lawan Linux? Unix yang mana?
terdon
1
Jawaban yang jelas adalah mv head.body.date.txt head_body_date.txt. Jika Anda memiliki batasan lain atau contoh nyata, silakan perbarui pertanyaan Anda dengan ini. Seperti yang ada sekarang, tidak jelas apakah pertanyaan tersebut merujuk ke file dengan nama eksplisit itu, direktori tunggal di mana semua file memiliki format tertentu, atau jika Anda ingin mencari hierarki direktori untuk nama-nama tertentu dan mengubahnya.
Kusalananda

Jawaban:

9

Ulangi nama file, dan gunakan Parameter ekspansi untuk konversi:

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

Pola ekspansi parameter, ${f//./_}menggantikan semua .s dengan _s dalam nama file ( $f).

Di atas akan melakukan proses kering, untuk memungkinkan penggantian nama sebenarnya terjadi, hapus echo:

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

Jika Anda ingin berurusan dengan ekstensi apa pun, tidak hanya .txt:

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

Setelah memeriksa hapus echountuk tindakan aktual:

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

Umum, untuk jumlah titik sembarang, setidaknya satu:

for f in *.*; do pre="${f%.*}"; suf="${f##*.}"; \
                 mv -i -- "$f" "${pre//./_}.${suf}"; done
heemayl
sumber
apakah ada cara umum jika ada jumlah titik yang sewenang-wenang?
Ciprian Tomoiagă
1
@CiprianTomoiaga Do:for f in *.*; do ...; done
heemayl
Saya mungkin menyarankan untuk *.*meluruskan pola dalam contoh, karena sekarang kode sepertinya berfungsi hanya dengan jumlah titik yang tetap.
ilkkachu
@ilkkachu Cukup adil, diedit.
heemayl
Terima kasih banyak heemay, itu berhasil untuk saya. dan beberapa pembelajaran!
SG
4

dengan perlberbasisrename

$ rename -n 's/\.[^.]+$(*SKIP)(*F)|\./_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)
  • \.[^.]+$(*SKIP)(*F) lewati pola ini dan cari kecocokan alternatif
  • |\./_/gganti semua .dengan_

Atau, menggunakan lookahead negatif

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

Setelah ini oke, hapus -nopsi

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

Gunakan penggantian regex untuk menemukan semua kecuali instance terakhir .dalam nama file (lookahead non-capturing) dan ganti dengan _. Generalise *.*jika Anda suka.

(versi khusus ini renametampaknya diinstal melalui util-linux). Saya menggunakan Ubuntu 12.04(ya, mesin sangat ketinggalan zaman).

Jonathan Carroll
sumber
2

bash:

old=head.body.date.txt oldb=${old%.*} olde=${old##*.} ; \
mv -- "$old" "${oldb//./_}.${olde}"
agc
sumber
(1) Anda harus menggunakan mv --, untuk menjaga terhadap nama file "lama" yang dimulai dengan -. (2) Anda harus memasukkan $oldtanda kutip ( "$old"), untuk menjaga dari nama file "lama" yang berisi spasi atau karakter yang khusus untuk shell (seperti *dan ?). (3) Jawaban Anda gagal dalam kasus yang agak patologis di mana ekstensi berisi garis bawah: head.body.date.t_xtdiubah namanya menjadi head_body_date_t.xt.
Scott
@Scott, terima kasih. Kode sekarang diubah sesuai dengan ketiga item yang disebutkan.
agc
1

Menggunakan mv, seddan revdalam satu baris:

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

Jika Anda ingin menerapkannya pada semua txtfile dalam rep saat ini, menggunakan gumpalan tampaknya rumit karena cara mvkerjanya, tetapi Anda dapat melakukan forloop dalam satu-liner:

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

Sedikit lebih lama, tetapi Anda dapat mencocokkan beberapa pola dalam lingkaran Anda!

Valentin B.
sumber
2
(1) Arrghhh! Jangan lakukan $(ls …); lakukan saja for file in *.txt. (2) Anda harus menggunakan lebih kutipan: mv "$file" "$(…)".
Scott
Terima kasih Scott, telah dicatat dan diedit! Saya masih anak-anak di shell, harap bersikap lembut;)
Valentin B.
Di belakang, menggunakan renameseperti beberapa orang lain telah menyarankan dalam jawaban mereka terdengar lebih sederhana ...
Valentin B.
-1
 mv head.body.date.txt head_body_date.txt

Ini sesederhana itu untuk satu file pada satu waktu. Anda tidak menentukan apakah Anda ingin ini dilakukan secara rekursif dalam suatu direktori atau pada keseluruhan direktori, sehingga itu akan menjadi solusi cepat dan paling nyaman bagi Anda.

coderodour
sumber