Jawaban lain telah membahas salah satu dari dua aspek utama dari pertanyaan ini: yaitu tentang bagaimana cara sukses menjalankan operasi penggantian nama yang Anda butuhkan. Tujuan dari jawaban ini adalah untuk menjelaskan mengapa perintah Anda tidak berfungsi, termasuk arti dari pesan kesalahan "bareword not allowed" yang aneh dalam konteks rename
perintah.
Bagian pertama dari jawaban ini adalah tentang hubungan antara rename
dan Perl dan bagaimana rename
menggunakan argumen baris perintah pertama yang Anda lewati, yang merupakan argumen kodenya. Bagian kedua adalah tentang bagaimana shell melakukan ekspansi - khususnya globbing - untuk membuat daftar argumen. Bagian ketiga adalah tentang apa yang terjadi dalam kode Perl yang memberikan kesalahan "Bareword not allowed". Akhirnya, bagian keempat adalah ringkasan dari semua langkah yang terjadi antara memasukkan perintah dan mendapatkan kesalahan.
1. Saat rename
memberi Anda pesan kesalahan aneh, tambahkan "Perl" ke pencarian Anda.
Di Debian dan Ubuntu, rename
perintahnya adalah skrip Perl yang melakukan penggantian nama file. Pada rilis yang lebih lama - termasuk 14,04 LTS, yang masih didukung sampai tulisan ini - itu adalah tautan simbolik yang menunjuk ( secara tidak langsung ) ke prename
perintah. Pada rilis yang lebih baru, alih-alih menunjuk ke file-rename
perintah yang lebih baru . Mereka dua perintah rename Perl bekerja sebagian besar dengan cara yang sama, dan saya hanya akan merujuk kepada mereka baik sebagai rename
untuk sisa jawaban ini.
Saat Anda menggunakan rename
perintah, Anda tidak hanya menjalankan kode Perl yang ditulis orang lain. Anda juga menulis kode Perl Anda sendiri dan menyuruhnya rename
menjalankannya. Ini karena argumen baris perintah pertama yang Anda berikan ke perintah, selain argumen opsi seperti , terdiri dari kode Perl aktualrename
-n
. The rename
perintah menggunakan kode ini untuk beroperasi pada masing-masing nama path yang Anda lulus sebagai argumen baris perintah berikutnya. (Jika Anda tidak memberikan argumen nama path, maka rename
bacalah nama path dari input standar , satu argumen per baris.)
Kode ini dijalankan dalam loop , yang iterates sekali per pathname. Di bagian atas setiap iterasi dari loop, sebelum kode Anda berjalan, $_
variabel khusus diberikan pathname yang sedang diproses. Jika kode Anda menyebabkan nilai $_
diubah menjadi sesuatu yang lain, maka file itu diganti namanya untuk memiliki nama baru.
Banyak ekspresi dalam Perl beroperasi secara implisit pada $_
variabel ketika mereka tidak diberi ekspresi lain untuk digunakan sebagai operan . Sebagai contoh, ekspresi substitusi $str =~ s/foo/bar/
perubahan kejadian pertama foo
dalam string yang diselenggarakan oleh $str
variabel untuk bar
, atau daun itu tidak berubah jika tidak mengandung foo
. Jika Anda hanya menulis s/foo/bar/
tanpa secara eksplisit menggunakan satu =~
operator yang , kemudian beroperasi pada $_
. Ini adalah s/foo/bar/
kependekan dari $_ =~ s/foo/bar/
.
Ini umum untuk lulus suatu s///
ekspresi untuk rename
sebagai argumen kode (yaitu, baris perintah argumen pertama), tetapi Anda tidak perlu. Anda dapat memberikan kode Perl apa pun yang Anda inginkan agar dijalankan di dalam loop, untuk memeriksa setiap nilai $_
dan (secara kondisional) memodifikasinya.
Ini memiliki banyak konsekuensi keren dan berguna , tetapi sebagian besar dari mereka jauh di luar cakupan pertanyaan dan jawaban ini. Alasan utama saya membawa ini ke sini - pada kenyataannya, alasan utama saya memutuskan untuk mengirim jawaban ini - adalah untuk membuat titik itu, karena argumen pertama rename
adalah kode Perl, kapan saja Anda mendapatkan pesan kesalahan aneh dan Anda kesulitan menemukan informasi tentang itu dengan mencari, Anda dapat menambahkan "Perl" ke string pencarian (atau bahkan mengganti "ganti nama" dengan "Perl", kadang-kadang) dan Anda akan sering menemukan jawaban.
2. Dengan rename *.DAT *.dat
, rename
perintah tidak pernah melihat *.DAT
!
Perintah seperti rename s/foo/bar/ *.txt
biasanya tidak lulus *.txt
sebagai argumen baris perintah ke rename
program, dan Anda tidak menginginkannya , kecuali jika Anda memiliki file yang namanya secara harfiah *.txt
, yang semoga tidak Anda lakukan.
rename
tidak menafsirkan pola gumpal seperti *.txt
, *.DAT
, *.dat
, x*
, *y
, atau *
ketika berlalu untuk itu sebagai argumen pathname. Sebagai gantinya, shell Anda melakukan ekspansi pathname pada mereka (yang juga disebut ekspansi nama file, dan juga disebut globbing). Ini terjadi sebelum rename
utilitas dijalankan. Shell memperluas gumpalan ke beberapa nama path yang berpotensi dan melewati semuanya, sebagai argumen baris perintah terpisah, untuk rename
. Di Ubuntu, shell interaktif Anda adalah Bash , kecuali Anda telah mengubahnya, itulah sebabnya saya ditautkan ke manual referensi Bash di atas.
Ada satu situasi di mana pola gumpalan dapat dilewatkan sebagai argumen baris perintah tunggal yang tidak diperluas untuk rename
: ketika tidak cocok dengan file apa pun. Kerang yang berbeda menunjukkan perilaku default yang berbeda dalam situasi ini, tetapi perilaku default Bash adalah untuk hanya lulus bola secara harfiah. Namun, Anda jarang menginginkan ini! Jika Anda menginginkannya, maka Anda harus memastikan bahwa polanya tidak diperluas, dengan mengutipnya . Ini berlaku untuk meneruskan argumen ke perintah apa pun, bukan hanya untuk rename
.
Mengutip tidak hanya untuk globbing (ekspansi nama file), karena ada ekspansi lain yang dilakukan shell Anda pada teks yang tidak dikutip dan, untuk sebagian dari mereka tetapi tidak pada yang lain , juga pada teks yang dilampirkan dalam "
"
tanda kutip. Secara umum, kapan saja Anda ingin memberikan argumen yang berisi karakter yang dapat diperlakukan secara khusus oleh shell, termasuk spasi, Anda harus mengutipnya, lebih disukai dengan '
'
kutipan .
Kode Perl s/foo/bar/
tidak mengandung apa pun yang diperlakukan secara khusus oleh shell, tetapi itu akan menjadi ide bagus bagi saya untuk mengutipnya juga - dan ditulis 's/foo/bar/'
. (Bahkan, satu-satunya alasan aku tidak adalah bahwa itu akan membingungkan untuk beberapa pembaca, karena saya belum berbicara tentang mengutip.) Alasan saya mengatakan ini akan menjadi baik karena itu sangat umum bahwa kode Perl tidak mengandung karakter seperti itu, dan jika saya mengubah kode itu, saya mungkin tidak ingat untuk memeriksa apakah kuotasi diperlukan. Sebaliknya, jika Anda ingin shell untuk memperluas bola, itu tidak harus dikutip.
3. Apa yang dimaksud penerjemah Perl dengan "bareword not allowed"
Pesan kesalahan yang Anda tunjukkan dalam pertanyaan Anda mengungkapkan bahwa, ketika Anda berlari rename *.DAT *.dat
, shell Anda diperluas *.DAT
ke daftar satu atau lebih nama file, dan bahwa yang pertama dari nama file itu b1.DAT
. Semua argumen selanjutnya - baik yang lain berkembang dari *.DAT
dan yang diperluas dari - *.dat
datang setelah argumen itu, sehingga mereka akan ditafsirkan sebagai nama path.
Karena apa yang benar-benar dijalankan adalah sesuatu seperti rename b1.DAT ...
, dan karena rename
memperlakukan argumen non-opsi pertamanya sebagai kode Perl, pertanyaannya menjadi: mengapa b1.DAT
kesalahan "bareword not allowed" ini muncul ketika Anda menjalankannya sebagai kode Perl?
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Dalam sebuah shell, kami mengutip string kami untuk melindunginya dari ekspansi shell yang tidak disengaja yang sebaliknya akan mengubahnya menjadi string lain secara otomatis (lihat bagian di atas). Shell adalah bahasa pemrograman tujuan khusus yang bekerja sangat berbeda dari bahasa tujuan umum (dan sintaksis dan semantik yang sangat aneh mencerminkannya). Tetapi Perl adalah bahasa pemrograman tujuan umum dan, seperti kebanyakan bahasa pemrograman tujuan umum, tujuan utama mengutip dalam Perl bukan untuk melindungi string, tetapi untuk menyebutkannya sama sekali. Ini sebenarnya cara sebagian besar bahasa pemrograman mirip dengan bahasa alami. Dalam bahasa Inggris, dan seandainya Anda memiliki anjing, "anjing Anda" adalah frasa dua kata, sedangkan anjing Anda adalah anjing. Demikian pula, dalam Perl, '$foo'
adalah string, sedangkan $foo
adalah sesuatu yang namanya $foo
.
Namun, tidak seperti hampir semua bahasa pemrograman tujuan umum lainnya, Perl juga terkadang akan menafsirkan teks yang tidak dikutip sebagai menyebutkan string - string yang "sama" dengan itu, dalam arti bahwa itu terdiri dari karakter yang sama dalam urutan yang sama. Itu hanya akan mencoba menafsirkan kode seperti itu jika itu adalah kata kunci (tidak $
atau sigil lainnya, lihat di bawah), dan setelah itu tidak dapat menemukan makna lain untuk memberikannya. Maka itu akan menganggapnya sebagai string, kecuali Anda mengatakannya tidak dengan mengaktifkan pembatasan .
Variabel dalam Perl biasanya dimulai dengan karakter tanda baca yang disebut sigil , yang menentukan tipe luas dari variabel. Misalnya, $
berarti skalar , @
berarti array , dan %
berarti hash . ( Ada yang lain. ) Jangan khawatir jika Anda merasa membingungkan (atau membosankan), karena saya hanya akan mengatakan bahwa ketika nama yang valid muncul dalam program Perl tetapi tidak didahului oleh sigil, nama itu dikatakan sebagai kata pengantar .
Barewords melayani berbagai tujuan, tetapi mereka biasanya menandakan fungsi built-in atau subrutin yang ditentukan pengguna yang telah didefinisikan dalam program (atau dalam modul yang digunakan oleh program). Perl tidak memiliki fungsi bawaan yang disebut b1
atau DAT
, jadi ketika penerjemah Perl melihat kode b1.DAT
, ia mencoba memperlakukan b1
dan DAT
sebagai nama subrutin. Dengan asumsi tidak ada subrutin seperti itu telah didefinisikan, ini gagal. Kemudian, asalkan pembatasan belum diaktifkan, itu memperlakukan mereka sebagai string. Ini akan berhasil, meskipun apakah Anda benar-benar ingin ini terjadi atau tidak, adalah dugaan siapa pun. Perl .
Operator merangkai string , sehingga b1.DAT
mengevaluasi ke stringb1DAT
. Artinya, b1.DAT
adalah cara yang buruk untuk menulis sesuatu seperti 'b1' . 'DAT'
atau "b1" . "DAT"
.
Anda dapat menguji ini sendiri dengan menjalankan perintah perl -E 'say b1.DAT'
, yang meneruskan skrip Perl pendek say b1.DAT
ke penerjemah Perl, yang menjalankannya, mencetak b1DAT
. (Dalam perintah itu, '
'
kutipan memberitahu shell untuk lulus say b1.DAT
sebagai argumen baris perintah tunggal, jika tidak, ruang akan menyebabkan say
dan b1.DAT
akan diuraikan sebagai kata yang terpisah dan perl
akan menerima mereka sebagai argumen yang terpisah. perl
Tidak tidak melihat tanda kutip sendiri, karena yang shell menghapusnya .)
Tapi sekarang, coba tulisuse strict;
di skrip Perl sebelumnya say
. Sekarang gagal dengan jenis kesalahan yang sama yang Anda dapatkan dari rename
:
$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.
Ini terjadi karena use strict;
melarang penerjemah Perl memperlakukan kata kunci bar sebagai string. Untuk melarang fitur khusus ini, itu benar-benar cukup untuk mengaktifkan subs
pembatasan saja. Perintah ini menghasilkan kesalahan yang sama seperti di atas:
perl -E 'use strict "subs"; say b1.DAT'
Tetapi biasanya programmer Perl hanya akan menulis use strict;
, yang memungkinkan subs
pembatasan dan dua lainnya. use strict;
umumnya direkomendasikan sebagai latihan. Jadi rename
perintah melakukan ini untuk kode Anda . Itu sebabnya Anda mendapatkan pesan kesalahan itu.
4. Singkatnya, inilah yang terjadi:
- Shell Anda dilewati
b1.DAT
sebagai argumen baris perintah pertama, yang rename
diperlakukan sebagai kode Perl untuk dijalankan dalam satu loop untuk setiap argumen pathname.
- Ini diartikan
b1
dan DAT
dihubungkan dengan .
operator.
b1
dan DAT
tidak diawali dengan sigils, jadi mereka diperlakukan sebagai kata pengantar.
- Kedua kata kunci itu akan dianggap sebagai nama fungsi bawaan atau subrutin yang ditentukan pengguna, tetapi tidak ada yang memiliki nama tersebut.
- Jika "subs ketat" tidak diaktifkan, maka mereka akan diperlakukan seperti ekspresi string
'b1'
dan 'DAT
'dan disatukan. Itu jauh dari yang Anda maksudkan, yang menerangi bagaimana fitur ini seringkali tidak membantu.
- Tapi "subs ketat" diaktifkan, karena
rename
memungkinkan semua pembatasan ( vars
, refs
, dan subs
). Karenanya, Anda malah mendapatkan kesalahan.
rename
berhenti karena kesalahan ini. Karena kesalahan semacam ini terjadi lebih awal, tidak ada upaya penggantian nama file yang dilakukan, meskipun Anda belum lulus -n
. Ini adalah hal yang baik, yang sering melindungi pengguna dari perubahan nama file yang tidak disengaja dan kadang-kadang bahkan dari kehilangan data aktual.
Terima kasih kepada Zanna , yang membantu saya mengatasi beberapa kekurangan penting dalam draft jawaban ini yang lebih awal . Tanpa dia, jawaban ini akan menjadi jauh lebih masuk akal, dan mungkin tidak diposting sama sekali.