คำตอบอื่น ๆได้กล่าวถึงประเด็นสำคัญข้อใดข้อหนึ่งในสองข้อนี้: วิธีการดำเนินการเปลี่ยนชื่อที่คุณต้องการให้สำเร็จ วัตถุประสงค์ของคำตอบนี้คือเพื่ออธิบายว่าทำไมคำสั่งของคุณไม่ทำงานรวมถึงความหมายของข้อความแสดงข้อผิดพลาด "bareword ไม่อนุญาต" แปลก ๆ ในบริบทของrename
คำสั่ง
ส่วนแรกของคำตอบนี้เกี่ยวกับความสัมพันธ์ระหว่างrename
และ Perl และวิธีการrename
ใช้อาร์กิวเมนต์บรรทัดคำสั่งแรกที่คุณส่งผ่านซึ่งเป็นอาร์กิวเมนต์รหัส ส่วนที่สองเป็นเรื่องเกี่ยวกับวิธีที่เชลล์ทำการขยาย - โดยเฉพาะอย่างยิ่งทำให้เป็นวงกลม - เพื่อสร้างรายการอาร์กิวเมนต์ ส่วนที่สามเกี่ยวกับสิ่งที่เกิดขึ้นในรหัส Perl ที่ให้ข้อผิดพลาด "Bareword ไม่อนุญาต" สุดท้ายส่วนที่สี่เป็นการสรุปขั้นตอนทั้งหมดที่เกิดขึ้นระหว่างการป้อนคำสั่งและรับข้อผิดพลาด
1. เมื่อrename
ให้ข้อความผิดพลาดแปลกเพิ่ม "Perl" ในการค้นหาของคุณ
ใน Debian และ Ubuntu rename
คำสั่งคือสคริปต์Perlที่ทำการเปลี่ยนชื่อไฟล์ สำหรับรีลีสที่เก่ากว่า - รวมถึง 14.04 LTS ซึ่งยังคงได้รับการสนับสนุนในขณะที่เขียนนี้ - มันเป็นลิงค์สัญลักษณ์ (ชี้ทางอ้อม ) ไปยังprename
คำสั่ง ในรีลีสที่ใหม่กว่าจะชี้ไปที่file-rename
คำสั่งที่ใหม่กว่า คำสั่งเปลี่ยนชื่อ Perl ทั้งสองนั้นทำงานเหมือนกันส่วนใหญ่และฉันจะอ้างถึงพวกเขาทั้งคู่ว่าเป็นrename
คำตอบที่เหลือ
เมื่อคุณใช้rename
คำสั่งคุณไม่เพียงแค่เรียกใช้โค้ด Perl ที่คนอื่นเขียน คุณกำลังเขียนโค้ด Perl ของคุณเองและบอกrename
ให้เรียกใช้ เพราะนี่คือครั้งแรกที่อาร์กิวเมนต์บรรทัดคำสั่งที่คุณส่งผ่านไปยังrename
คำสั่งอื่น ๆ นอกเหนือจากข้อโต้แย้งตัวเลือกเช่น-n
,ประกอบด้วยรหัส Perl ที่เกิดขึ้นจริง rename
คำสั่งใช้รหัสนี้ในการดำเนินงานในแต่ละpathnamesที่คุณผ่านมันเป็นอาร์กิวเมนต์บรรทัดคำสั่งที่ตามมา (หากคุณไม่ผ่านอาร์กิวเมนต์ชื่อพา ธ ใด ๆ ให้rename
อ่านชื่อพา ธ จากอินพุตมาตรฐานแทนหนึ่งรายการต่อบรรทัด)
รหัสถูกเรียกใช้ภายในลูปซึ่งวนซ้ำหนึ่งครั้งต่อชื่อพา ธ ที่ด้านบนของการวนซ้ำแต่ละครั้งก่อนที่โค้ดของคุณจะรัน$_
ตัวแปรพิเศษจะถูกกำหนดชื่อพา ธ ที่กำลังประมวลผลอยู่ หากรหัสของคุณทำให้ค่า$_
ที่จะเปลี่ยนเป็นอย่างอื่นไฟล์นั้นจะถูกเปลี่ยนชื่อให้มีชื่อใหม่
หลายคนแสดงออกใน Perl ทำงานโดยปริยายใน$_
ตัวแปรเมื่อพวกเขาจะไม่ได้รับการแสดงออกอื่น ๆ ที่จะใช้เป็นตัวถูกดำเนินการ ยกตัวอย่างเช่นการแสดงออกทดแทน$str =~ s/foo/bar/
การเปลี่ยนแปลงเกิดขึ้นครั้งแรกของfoo
ในสตริงที่จัดขึ้นโดย$str
ตัวแปรที่จะหรือใบมันไม่เปลี่ยนแปลงถ้ามันไม่ได้มีbar
foo
หากคุณเพียงแค่เขียนs/foo/bar/
โดยไม่ต้องชัดเจนโดยใช้ผู้ประกอบการแล้วมันทำงานบน นี้คือจะบอกว่าเป็นชื่อสั้น=~
$_
s/foo/bar/
$_ =~ s/foo/bar/
เป็นเรื่องปกติที่จะผ่านแสดงออกจะเป็นอาร์กิวเมนต์รหัส (เช่นอาร์กิวเมนต์บรรทัดคำสั่งครั้งแรก) แต่คุณไม่จำเป็นต้อง คุณสามารถให้รหัส Perl ใด ๆ ที่คุณต้องการให้มันทำงานภายในลูปเพื่อตรวจสอบแต่ละค่าของและ (ตามเงื่อนไข) แก้ไขมันs///
rename
$_
สิ่งนี้มีผลลัพธ์ที่ยอดเยี่ยมและเป็นประโยชน์มากมาย แต่ส่วนใหญ่ของพวกเขานั้นดีเกินกว่าขอบเขตของคำถามและคำตอบนี้ เหตุผลหลักที่ฉันนำมาไว้ที่นี่ - ในความเป็นจริงเหตุผลหลักที่ฉันตัดสินใจโพสต์คำตอบนี้ - คือการทำให้จุดที่เพราะอาร์กิวเมนต์แรกที่rename
เป็นจริงรหัส Perl ทุกครั้งที่คุณได้รับข้อความผิดพลาดแปลกและคุณ มีปัญหาในการค้นหาข้อมูลเกี่ยวกับมันโดยการค้นหาคุณสามารถเพิ่ม "Perl" ลงในสตริงการค้นหา (หรือแม้แต่แทนที่ "เปลี่ยนชื่อ" ด้วย "Perl" บางครั้ง) และคุณมักจะพบคำตอบ
2. rename *.DAT *.dat
การrename
สั่งไม่เคยเห็น*.DAT
!
โดยทั่วไปคำสั่งเช่นrename s/foo/bar/ *.txt
นั้นจะไม่ส่งผ่าน*.txt
เป็นอาร์กิวเมนต์บรรทัดคำสั่งไปยังrename
โปรแกรมและคุณไม่ต้องการให้เว้นเสียแต่ว่าคุณจะมีไฟล์ที่มีชื่อเป็นตัวอักษร*.txt
ซึ่งหวังว่าคุณจะไม่
rename
ไม่ได้ตีความรูปแบบ globชอบ*.txt
, *.DAT
, *.dat
, x*
, *y
หรือ*
เมื่อผ่านไปเป็นข้อโต้แย้งชื่อพา ธ แต่เชลล์ของคุณจะทำการขยายชื่อพา ธบนมัน (ซึ่งเรียกว่าการขยายชื่อไฟล์และเรียกอีกอย่างว่าการทำให้กลม) สิ่งนี้เกิดขึ้นก่อนที่rename
ยูทิลิตี้จะทำงาน เปลือกขยาย globs เข้า pathnames rename
ที่อาจเกิดขึ้นหลายครั้งและผ่านพวกเขาทั้งหมดเป็นอาร์กิวเมนต์บรรทัดคำสั่งที่แยกจากกันไป ใน Ubuntu เชลล์แบบโต้ตอบของคุณคือBashยกเว้นว่าคุณได้ทำการเปลี่ยนแปลงซึ่งเป็นสาเหตุที่ฉันเชื่อมโยงไปยังคู่มืออ้างอิง Bashด้านบน
มีสถานการณ์หนึ่งที่รูปแบบ glob อาจถูกส่งผ่านเป็นอาร์กิวเมนต์บรรทัดคำสั่งเดียวที่ยังไม่ขยายไปที่rename
: เมื่อมันไม่ตรงกับไฟล์ใด ๆ กระสุนที่แตกต่างกันแสดงพฤติกรรมเริ่มต้นที่แตกต่างกันในสถานการณ์นี้ แต่พฤติกรรมเริ่มต้นของ Bash คือการส่งผ่าน glob อย่างแท้จริง อย่างไรก็ตามคุณไม่ค่อยต้องการสิ่งนี้! หากคุณไม่ต้องการมันแล้วคุณควรตรวจสอบให้แน่ใจว่ารูปแบบจะไม่ขยายตัวโดยอ้างว่ามัน นี้ไปสำหรับการส่งผ่านข้อโต้แย้งคำสั่งใด ๆ rename
ที่ไม่เพียงเพื่อ
อ้างไม่ได้เป็นเพียง globbing (ส่วนขยายชื่อไฟล์) เนื่องจากมีการขยายตัวอื่น ๆที่มีประสิทธิภาพเปลือกของคุณในข้อความ unquoted และสำหรับบางส่วนของพวกเขา แต่คนอื่นไม่ได้นอกจากนี้ยังมีข้อความที่อยู่ใน"
"
คำพูด โดยทั่วไปทุกเวลาที่คุณต้องการที่จะผ่านการโต้แย้งที่มีตัวละครที่อาจจะได้รับการปฏิบัติเป็นพิเศษโดยเปลือกรวมถึงช่องว่างคุณควรพูดมันยิ่งกับคำพูด'
'
รหัส Perl s/foo/bar/
ไม่ได้มีอะไรที่ได้รับการปฏิบัติเป็นพิเศษโดยเปลือก แต่ก็จะได้รับความคิดที่ดีสำหรับผมที่จะได้ยกมาที่มากเกินไป - 's/foo/bar/'
และการเขียน (ในความเป็นจริงเหตุผลเดียวที่ฉันไม่ได้ก็คือว่ามันจะทำให้เกิดความสับสนให้กับผู้อ่านบางคนที่ผมยังไม่ได้พูดคุยเกี่ยวกับ quoting.) เหตุผลที่ผมพูดนี้จะได้รับที่ดีเป็นเพราะมันเป็นเรื่องธรรมดามากว่ารหัส Perl ไม่ประกอบด้วย ตัวอักษรดังกล่าวและถ้าฉันต้องเปลี่ยนรหัสนั้นฉันอาจจำไม่ได้ว่าตรวจสอบว่าจำเป็นต้องมีการอ้างอิงหรือไม่ ในทางตรงกันข้ามถ้าคุณต้องการให้เปลือกขยายตัวกลมมันจะต้องไม่ถูกยกมา
3. ล่ามภาษา Perl แปลว่า "ห้ามใช้คำ bareword"
ข้อผิดพลาดที่คุณแสดงให้เห็นในคำถามของคุณแสดงให้เห็นว่าเมื่อคุณวิ่งrename *.DAT *.dat
, เปลือกของคุณขยายตัว*.DAT
ในรายการหนึ่งหรือมากกว่าหนึ่งชื่อไฟล์และที่แรกb1.DAT
ของชื่อไฟล์เหล่านั้น อาร์กิวเมนต์ที่ตามมาทั้งหมด - ทั้งคู่ถูกขยายจาก*.DAT
และขยายจาก - มา*.dat
หลังจากอาร์กิวเมนต์นั้นดังนั้นพวกเขาจะถูกตีความว่าเป็นชื่อพา ธ
เพราะสิ่งที่วิ่งจริง ๆ แล้วเป็นเช่นrename b1.DAT ...
นั้นและเนื่องจากrename
ถือเป็นอาร์กิวเมนต์ที่ไม่ใช่ตัวเลือกตัวแรกเป็นรหัส Perl คำถามจะกลายเป็น: ทำไมb1.DAT
ข้อผิดพลาด "bareword ไม่อนุญาต" เหล่านี้เมื่อคุณเรียกใช้เป็นรหัส 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).
ในเชลล์เราเสนอราคาสตริงของเราเพื่อปกป้องพวกเขาจากการขยายเชลล์โดยไม่ตั้งใจซึ่งจะเปลี่ยนพวกมันเป็นสตริงอื่นโดยอัตโนมัติ (ดูหัวข้อด้านบน) เชลล์เป็นภาษาโปรแกรมวัตถุประสงค์พิเศษที่ทำงานแตกต่างจากภาษาที่ใช้งานทั่วไปมาก (และไวยากรณ์และความหมายแปลก ๆของมันสะท้อนให้เห็น) แต่ Perl เป็นภาษาโปรแกรมทั่วไปและเช่นเดียวกับภาษาโปรแกรมทั่วไปส่วนใหญ่จุดประสงค์หลักของการเขียนข้อความใน Perl ไม่ใช่เพื่อปกป้องสตริง แต่พูดถึงมันทั้งหมด นี่เป็นวิธีที่ภาษาการเขียนโปรแกรมส่วนใหญ่คล้ายกับภาษาธรรมชาติ. ในภาษาอังกฤษและสมมติว่าคุณมีสุนัข "สุนัขของคุณ" เป็นวลีสองคำในขณะที่สุนัขของคุณเป็นสุนัข ในทำนองเดียวกันในเพิร์ล'$foo'
เป็นสตริงในขณะที่บางสิ่งบางอย่างที่มีชื่อเป็น$foo
$foo
แต่แตกต่างเพียงเกี่ยวกับทุกอื่น ๆ วัตถุประสงค์ทั่วไปการเขียนโปรแกรมภาษา Perl จะยังบางครั้งการตีความข้อความ unquoted เป็นกล่าวขวัญสตริง - สตริงที่เป็น "เดียวกัน" ขณะที่มันในแง่ที่ว่ามันถูกสร้างขึ้นจากตัวละครเดียวกันใน คำสั่งเดียวกัน มันจะพยายามตีความรหัสด้วยวิธีนั้นถ้าเป็น bareword (ไม่มี$
หรือ sigil อื่น ๆ , ดูด้านล่าง) และหลังจากที่มันไม่สามารถหาความหมายอื่นใดที่จะให้มัน จากนั้นก็จะใช้มันเป็นสตริงจนกว่าคุณจะบอกว่ามันไม่ได้ไปด้วยการทำให้ข้อ จำกัด
โดยทั่วไปแล้วตัวแปรใน Perl จะเริ่มต้นด้วยอักขระคั่นที่เรียกว่าsigilซึ่งระบุประเภทของตัวแปร ยกตัวอย่างเช่น$
หมายความว่าสเกลาร์ , @
วิธีอาร์เรย์และ%
วิธีกัญชา ( มีคนอื่น ๆ ) ไม่ต้องกังวลหากคุณพบว่าสับสน (หรือน่าเบื่อ) เพราะฉันแค่นำมันมาพูดว่าเมื่อชื่อที่ถูกต้องปรากฏในโปรแกรม Perl แต่ไม่ได้นำหน้าด้วยชื่อนั้น มีการกล่าวถึงเป็น bareword
Barewords ให้บริการตามวัตถุประสงค์ต่าง ๆ แต่โดยปกติแล้วพวกเขาหมายถึงฟังก์ชั่นในตัวหรือรูทีนย่อยที่ผู้ใช้กำหนดซึ่งได้รับการกำหนดในโปรแกรม (หรือในโมดูลที่ใช้โดยโปรแกรม) Perl ไม่มีฟังก์ชั่นในตัวที่เรียกว่าb1
หรือDAT
ดังนั้นเมื่อล่าม Perl เห็นรหัสb1.DAT
ก็พยายามที่จะรักษาb1
และDAT
เป็นชื่อของรูทีนย่อย สมมติว่าไม่มีการกำหนดรูทีนย่อยดังกล่าวสิ่งนี้จะล้มเหลว จากนั้นข้อ จำกัด ที่ให้ไว้ยังไม่ได้เปิดใช้งานจะถือว่าเป็นข้อ จำกัด สิ่งนี้จะใช้ได้แม้ว่าคุณตั้งใจจะทำสิ่งนี้จริงหรือไม่ก็เป็นสิ่งที่เดาได้ .
ผู้ประกอบการของ Perl เชื่อมสตริงเข้าด้วยกันดังนั้นb1.DAT
ประเมินค่าสตริงb1DAT
. นั่นคือb1.DAT
เป็นวิธีที่ดีที่จะเขียนสิ่งที่ต้องการหรือ'b1' . 'DAT'
"b1" . "DAT"
คุณสามารถทดสอบนี้ออกเองโดยใช้คำสั่งperl -E 'say b1.DAT'
ที่ผ่าน Perl สคริปต์สั้นsay b1.DAT
เพื่อล่าม Perl b1DAT
ซึ่งไหลมันพิมพ์ (ในคำสั่งนั้น'
'
คำพูดที่บอกเปลือกจะผ่านsay b1.DAT
เป็นอาร์กิวเมนต์บรรทัดคำสั่งเดียวมิฉะนั้นพื้นที่ที่จะก่อให้เกิดsay
และb1.DAT
จะได้รับการแยกวิเคราะห์เป็นคำที่แยกจากกันและperl
จะได้รับพวกเขาเป็นข้อโต้แย้งที่แยกต่างหาก. perl
ไม่ได้เห็นคำพูดของตัวเองเป็นเชลล์ลบออก )
แต่ตอนนี้ลองเขียนuse strict;
ในสคริปต์ Perl say
ก่อน ตอนนี้มันล้มเหลวด้วยข้อผิดพลาดชนิดเดียวกันที่คุณได้รับจาก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.
สิ่งนี้เกิดขึ้นเพราะuse strict;
ห้ามล่าม Perl ไม่ให้ถือ barewords เป็นสตริง หากต้องการห้ามฟีเจอร์นี้มันจะเพียงพอที่จะเปิดใช้งานsubs
ข้อ จำกัด คำสั่งนี้สร้างข้อผิดพลาดเช่นเดียวกับข้างบน:
perl -E 'use strict "subs"; say b1.DAT'
แต่โดยปกติโปรแกรมเมอร์ Perl จะเพิ่งเขียนuse strict;
ซึ่งทำให้subs
ข้อ จำกัด และอีกสองคน use strict;
เป็นการปฏิบัติที่แนะนำโดยทั่วไป ดังนั้นrename
คำสั่งทำสิ่งนี้เพื่อรหัสของคุณ นั่นคือเหตุผลที่คุณได้รับข้อความแสดงข้อผิดพลาด
4. โดยสรุปนี่คือสิ่งที่เกิดขึ้น:
- เชลล์ของคุณส่งผ่าน
b1.DAT
เป็นอาร์กิวเมนต์บรรทัดคำสั่งแรกซึ่งrename
ถือว่าเป็นรหัส Perl เพื่อทำงานเป็นวนรอบสำหรับแต่ละอาร์กิวเมนต์ชื่อพา ธ
- สิ่งนี้ถูกนำไปหมายถึง
b1
และDAT
เชื่อมต่อกับ.
ผู้ประกอบการ
b1
และDAT
ไม่ได้ขึ้นต้นด้วย sigils ดังนั้นจึงถือว่าเป็น barewords
- barewords ทั้งสองนั้นจะถูกใช้เป็นชื่อของฟังก์ชันในตัวหรือรูทีนย่อยที่ผู้ใช้กำหนด แต่ไม่มีสิ่งใดที่มีชื่อเหล่านั้น
- หากไม่ได้เปิดใช้งาน "เข้มงวดส่วนย่อย" พวกเขาจะได้รับการปฏิบัติเช่นเดียวกับการแสดงออกของสตริง
'b1'
และ'DAT
'และเชื่อมต่อกัน ซึ่งอยู่ไกลจากสิ่งที่คุณตั้งใจจะให้แสงสว่างซึ่งคุณสมบัตินี้มักจะไม่เป็นประโยชน์
- แต่ "ผู้ใต้บังคับบัญชาอย่างเคร่งครัด" ถูกเปิดใช้งานเพราะ
rename
ช่วยให้ทุกข้อ จำกัด ( vars
, refs
และsubs
) ดังนั้นคุณได้รับข้อผิดพลาดแทน
rename
ออกจากเนื่องจากข้อผิดพลาดนี้ -n
เพราะการจัดเรียงของข้อผิดพลาดนี้เกิดขึ้นในช่วงต้นไม่มีการเปลี่ยนชื่อไฟล์พยายามทำให้ถึงแม้ว่าคุณไม่ได้ผ่าน นี่เป็นสิ่งที่ดีซึ่งมักจะปกป้องผู้ใช้จากการเปลี่ยนชื่อไฟล์โดยไม่ตั้งใจและบางครั้งอาจสูญหายจากข้อมูลจริง
ขอบคุณไปที่Zannaที่ช่วยฉันจัดการข้อบกพร่องที่สำคัญหลายอย่างในแบบร่างที่ดีกว่าของคำตอบนี้ หากไม่มีเธอคำตอบนี้น่าจะสมเหตุสมผลน้อยลงและอาจไม่สามารถโพสต์ได้เลย