Pertanyaan Mengganti nama sejumlah besar file gambar dengan bash


Saya perlu mengganti nama sekitar. 70.000 file. Sebagai contoh: Dari sb_606_HBO_DPM_0089000 untuk sb_606_dpm_0089000 dll.

Kisaran angka berasal 0089000 untuk 0163022. Itu hanya bagian pertama dari nama yang perlu diubah. semua file berada dalam satu direktori, dan diberi nomor secara berurutan (urutan gambar). Angka-angka harus tetap tidak berubah.

Ketika saya mencoba ini di bash, grigin ke saya bahwa 'Daftar argumen terlalu panjang'.

Edit:

Saya pertama kali mencoba mengganti nama file tunggal dengan mv:

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

Lalu saya mencoba mengganti nama rentang (saya pelajari di sini minggu lalu bagaimana memindahkan banyak file, jadi saya pikir sintaks yang sama mungkin berfungsi untuk mengganti nama file ...). saya berpikir Saya mencoba yang berikut ini (atau sesuatu seperti itu):

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

16
2018-05-19 14:13


asal


Untuk pengulas: Saya rasa ini bukan duplikat; sebagian besar jawaban CLI pada pertanyaan lain tidak akan berfungsi di sini karena banyaknya file yang bertabrakan dengan shell ARG_MAX membatasi. Karena pertanyaan ini secara eksplisit meminta solusi baris perintah, (mungkin sama) solusi GUI seperti pada pertanyaan lain juga tidak cocok. - dessert
Saya tidak berpikir ini adalah penipuan karena tidak apa-apa untuk memiliki lebih dari satu pertanyaan tentang penggantian nama file. Tolong jangan menutup pertanyaan khusus terhadap sumber daya generik yang tidak benar-benar menjawabnya ... - Zanna
@rich Jika Anda dapat mengedit secara eksplisit perintah apa yang Anda coba, maka akan lebih jelas bahwa ini bukan dupe. (Ini menunjukkan kepada kita bahwa Anda menyadari pendekatan ini.) Cheers. - Sparhawk
kaya, pertanyaan Anda bukan penipuan, karena itu pertanyaan spesifik. Jangan khawatir tentang itu. Lebih penting lagi, setelah sebuah pertanyaan telah menerima sejumlah jawaban yang upvoted, mengeditnya mungkin bukan ide yang baik karena suntingan Anda mungkin membuat jawaban yang ada kurang valid. Sekarang saya merasa jawaban saya harus menjelaskan mengapa mv {1..2} {3..4} tidak berfungsi, yang merupakan masalah yang berbeda ARG_MAX... Semua orang yang menjawab mungkin akan merasakan hal yang sama! Jadi, dari sudut pandang saya, saya berharap Anda akan mengembalikan suntingan terakhir Anda dan, jika Anda mau, tanyakan pertanyaan baru tentang mving dengan rentang - Zanna
@Sparhawk OP menulis dengan cukup jelas, dari versi pertama pertanyaan, bahwa masalahnya adalah argument list too long kesalahan. Tidak perlu menjelaskan lebih lanjut, ini jelas bukan penipuan karena kita memerlukan solusi untuk menangani ARG_MAX dan jawaban dalam duplikat yang diusulkan tidak melakukan itu. - terdon♦


Jawaban:


Salah satu caranya adalah menggunakan find dengan -exec, dan + pilihan. Ini membangun daftar argumen, tetapi memecah daftar menjadi banyak panggilan yang diperlukan untuk beroperasi pada semua file tanpa melebihi daftar argumen maksimum. Sangat cocok ketika semua argumen akan diperlakukan sama. Ini kasusnya dengan rename, meskipun tidak dengan mv.

Anda mungkin perlu memasang nama Perl:

sudo apt install rename

Kemudian Anda dapat menggunakan, misalnya:

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

Menghapus -n setelah pengujian, untuk benar-benar mengganti nama file.


25
2018-05-19 14:26





Saya akan menyarankan tiga alternatif. Masing-masing adalah perintah satu baris sederhana, tetapi saya akan memberikan varian untuk kasus yang lebih rumit, terutama jika file yang diproses dicampur dengan file lain dalam directrory yang sama.

mmv

Saya akan menggunakan perintah mmv dari paket dengan nama yang sama:

mmv '*HBO_DPM*' '#1dpm#2'

Perhatikan bahwa argumen dilewatkan sebagai string, sehingga ekspansi glob tidak terjadi di shell. Perintah menerima tepat dua argumen, dan kemudian menemukan file yang sesuai secara internal, tanpa batasan ketat pada jumlah file. Perhatikan juga bahwa perintah di atas mengasumsikan bahwa semua file yang cocok dengan glob pertama akan diganti namanya. Tentu Anda bebas untuk lebih spesifik:

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

Jika Anda memiliki file di luar kisaran nomor yang diminta dalam direktori yang sama, Anda mungkin lebih baik dengan loop atas angka yang diberikan lebih jauh dalam jawaban ini. Namun Anda juga bisa menggunakan urutan panggilan mmv dengan pola yang sesuai:

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

mengulang angka

Jika Anda ingin menghindari pemasangan apa pun, atau perlu memilih dengan rentang nomor untuk menghindari kecocokan di luar rentang ini, dan Anda siap menunggu 74.023 permintaan perintah, Anda dapat menggunakan loop bash polos:

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

Ini bekerja sangat baik di sini karena tidak ada celah dalam urutan. Kalau tidak, Anda mungkin ingin memeriksa apakah file sumber benar-benar ada.

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

Perhatikan bahwa berbeda dengan for ((i=89000; i<=163022; ++i)) ekspansi brace tidak menangani nol terkemuka sejak beberapa Bash rilis beberapa tahun yang lalu. Sebenarnya ada perubahan yang saya minta, jadi saya senang melihat kasus penggunaan untuk itu.

Bacaan lebih lanjut:  Ekspansi Brace di halaman info Bash, terutama bagian tentang {x..y[..incr]}.

menggilir file

Pilihan lain adalah menggilir gumpalan yang cocok, bukan hanya mengulang di atas rentang bilangan bulat yang dimaksud. Sesuatu seperti ini:

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

Sekali lagi ini adalah satu mv doa per file. Dan sekali lagi, perulangannya melewati daftar panjang elemen, tetapi seluruh daftar tidak dilewatkan sebagai argumen untuk subproses, tetapi ditangani secara internal oleh bash, jadi batasnya tidak akan menyebabkan masalah bagi Anda.

Bacaan lebih lanjut:  Ekspansi Parameter Shell di halaman info Bash, mendokumentasikan ${parameter/pattern/string} diantara yang lain.

Jika Anda ingin membatasi rentang angka yang Anda berikan, Anda bisa menambahkan cek untuk itu:

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

Sini ${i##pattern} menghapus pencocokan awalan terpanjang pattern dari $i. Awalan terpanjang didefinisikan sebagai apa pun, kemudian garis bawah, lalu nol atau lebih nol. Yang terakhir ditulis sebagai *(0) yang merupakan perpanjangan pola glob itu tergantung pada extglob pilihan sedang diatur. Menghapus nol terkemuka penting untuk memperlakukan nomor sebagai basis 10 bukan basis 8. The +([0-9]) dalam argumen loop adalah gumpalan yang diperluas, yang cocok dengan satu atau lebih digit, kalau-kalau Anda memiliki file di sana yang memulai hal yang sama tetapi tidak berakhir dalam angka.


11
2018-05-19 19:05



Terima kasih! Ini bekerja seperti mimpi: untuk saya di {0089000..0163022}; lakukan mv sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; selesai - saya harus menambahkan ekstensi nama file untuk membuatnya bekerja, tetapi itu hanya apa yang saya inginkan dan saya bahkan mengerti sintaks. Terima kasih @MvG - rich
@rich: Senang saya bisa membantu - Anda dan mudah-mudahan pengunjung di masa mendatang juga. Jangan lupa untuk menerima jawaban yang paling berguna. Anda selalu dapat mengubah tanda centang itu di masa depan jika ada sesuatu yang lebih baik. - MvG


Salah satu cara untuk bekerja di sekitar ARG_MAX limit adalah menggunakan builtin bash shell printf:

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

Ex.

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

tapi

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

10
2018-05-19 15:07





find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \;
./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000

find di direktori saat ini . untuk semua file -type f dan lakukan rename file yang ditemukan $1 dengan mengganti HBO_DPM dengan dmp  satu per satu -exec ... \;

menggantikan echo dengan mv untuk melakukan rename.


7
2018-05-19 14:34





Anda bisa menulis skrip python kecil, sesuatu seperti:

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

Simpan itu sebagai file teks sebagai rename.py di folder file-file berada, lalu dengan terminal di folder itu:

python rename.py

6
2018-05-20 01:37





Anda dapat melakukannya secara file (mungkin butuh waktu) dengan

sudo apt install util-linux  # if you don't have it already
for i in *; do rename.ul HBO_DPM dpm "$i"; done

Seperti Perl rename digunakan dalam jawaban lain, rename.ul juga merupakan pilihan -n atau --no-act untuk pengujian.


6
2018-05-19 14:30



Saya telah mengedit komentar Anda tentang jawaban Zanna, silakan edit jawaban Zanna atau tinggalkan komentar. - ubashu
@ubashu itu bukan komentar atas jawaban saya - itu mengacu pada -n bendera yang saya gunakan untuk menguji dan menyarankannya dapat digunakan di rename.ul terlalu. - Zanna


Saya melihat bahwa tidak ada yang mengundang sahabat saya sed ke pesta :). Pengikut for lingkaran akan mencapai tujuan Anda:

for i in sb_606_HBO_DPM*; do
  mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')";
done

Ada banyak alat untuk pekerjaan semacam itu, pilih salah satu yang paling bisa dimengerti untuk Anda. Yang ini sederhana dan mudah diubah sesuai dengan ini atau tujuan lain ...


3
2018-05-20 03:57



Memang, tidak terlalu relevan dalam kasus spesifik ini, tetapi ini akan gagal jika salah satu nama file mengandung baris baru. Saya menyebutkan ini karena sebagian besar (semua?) Jawaban lain yang kuat dan dapat menangani nama file yang sewenang-wenang, atau hanya bekerja pada skema penamaan file OP. - terdon♦
... baris baru, spasi, wildcard, ... beberapa di antaranya dapat dihindari dengan mengutip $i dalam substitusi perintah, tetapi tidak ada cara mudah untuk menangani baris baru di belakang dalam nama file. - muru


Karena kami memberikan opsi, inilah pendekatan Perl. cd ke dalam direktori target dan jalankan:

perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}'

Penjelasan

  • perl -e : jalankan skrip yang diberikan oleh -e.
  • foreach(glob){} : jalankan apa pun yang ada di { } pada setiap hasil glob.
  • glob("sb_*") : kembalikan daftar semua file dan direktori di direktori saat ini yang namanya cocok dengan glob shell sb*.
  • rename $_, s/_HBO_DPM_/_dpm_/r : perl magic. $_ adalah variabel khusus yang memegang setiap elemen yang kita iterasi (dalam foreach). Jadi di sini, akan ada setiap file yang ditemukan. s/_HBO_DPM_/_dpm_/ menggantikan kejadian pertama _HBO_DPM_ dengan _dpm_. Ini berjalan $_ secara default, sehingga akan berjalan pada setiap nama file. Itu /r berarti "menerapkan penggantian ini ke salinan string target (nama file) dan mengembalikan string yang dimodifikasi. rename melakukan apa yang Anda harapkan: ia mengganti nama file. Jadi semuanya akan mengganti nama file saat ini ($_) untuk dirinya sendiri dengan _HBO_DPM_ digantikan oleh _dpm_.

Anda dapat menulis hal yang sama dengan skrip yang diperluas (dan yang lebih mudah dibaca):

#! /usr/bin/env perl
use strict;
use warnings;

foreach my $fileName (glob("sb_*")){
  ## Copy the name to a new variable
  my $newName = $fileName;
  ## change the copy. $newName is now the changed version
  $newName =~ s/_HBO_DPM_/_dpm_/;
  ## rename
  rename $fileName, $newName;
}

3
2018-05-22 10:52





Tergantung pada jenis penggantian nama yang Anda inginkan, gunakan vidir dengan beberapa pengeditan baris mungkin memuaskan.
Dalam kasus khusus Anda, Anda dapat memilih semua baris di editor teks Anda dan menghapus _ "HBO " bagian dari nama file dalam beberapa penekanan tombol.


1
2018-05-19 16:25



ya, vi telah menemukan dan mengganti gloable. - Jasen
Bisakah Anda memperpanjang jawaban Anda dan memberikan contoh bagaimana mencapai tujuan OP dengan vidir? - dessert