Các câu trả lời khác đã giải quyết một trong hai khía cạnh chính của câu hỏi này: đó là làm thế nào để thực hiện thành công thao tác đổi tên mà bạn cần. Mục đích của câu trả lời này là để giải thích lý do tại sao các lệnh của bạn không hoạt động, bao gồm cả ý nghĩa của thông báo lỗi "bareword không được phép" kỳ lạ đó trong ngữ cảnh của rename
lệnh.
Phần đầu tiên của câu trả lời này là về mối quan hệ giữa rename
và Perl và cách rename
sử dụng đối số dòng lệnh đầu tiên bạn truyền cho nó, đó là đối số mã của nó. Phần thứ hai là về cách trình bao thực hiện các mở rộng - cụ thể là toàn cầu hóa - để xây dựng một danh sách đối số. Phần thứ ba là về những gì đang diễn ra trong mã Perl có lỗi "Bareword không được phép". Cuối cùng, phần thứ tư là một bản tóm tắt của tất cả các bước diễn ra giữa việc nhập lệnh và nhận lỗi.
1. Khi rename
cung cấp cho bạn thông báo lỗi lạ, hãy thêm "Perl" vào tìm kiếm của bạn.
Trong Debian và Ubuntu, rename
lệnh là tập lệnh Perl thực hiện đổi tên tệp. Trên các bản phát hành cũ hơn - bao gồm 14.04 LTS, vẫn được hỗ trợ khi viết bài này - đó là một liên kết tượng trưng chỉ ( gián tiếp ) đến prename
lệnh. Trên các bản phát hành mới hơn, nó thay vào đó là file-rename
lệnh mới hơn . Những hai lệnh đổi tên Perl làm việc chủ yếu là theo cùng một cách, và tôi sẽ chỉ đề cập đến cả hai như rename
cho phần còn lại của câu trả lời này.
Khi bạn sử dụng rename
lệnh, bạn không chỉ chạy mã Perl mà người khác đã viết. Bạn cũng đang viết mã Perl của riêng bạn và bảo rename
chạy nó. Điều này là do đối số dòng lệnh đầu tiên bạn truyền cho lệnh, ngoài các đối số tùy chọn như , bao gồm mã Perl thực tếrename
-n
. Các rename
lệnh sử dụng mã này để hoạt động trên mỗi tên đường dẫn mà bạn vượt qua nó như các đối số dòng lệnh tiếp theo. (Nếu bạn không vượt qua bất kỳ đối số tên đường dẫn nào, thì hãy rename
đọc tên đường dẫn từ đầu vào tiêu chuẩn , thay vào đó là một đối số trên mỗi dòng.)
Mã được chạy bên trong một vòng lặp , lặp lại một lần cho mỗi tên đường dẫn. Ở đầu mỗi lần lặp của vòng lặp, trước khi mã của bạn chạy, $_
biến đặc biệt được gán tên đường dẫn hiện đang được xử lý. Nếu mã của bạn khiến giá trị của $_
bị thay đổi thành một thứ khác, thì tệp đó được đổi tên thành tên mới.
Nhiều biểu thức trong Perl hoạt động ngầm định trên $_
biến khi chúng không được cung cấp bất kỳ biểu thức nào khác để sử dụng làm toán hạng . Ví dụ, biểu thức thay thế thay $str =~ s/foo/bar/
đổi lần xuất hiện đầu tiên của foo
chuỗi được giữ bởi $str
biến thành bar
hoặc không thay đổi nếu nó không chứa foo
. Nếu bạn chỉ cần viết s/foo/bar/
mà không sử dụng một cách rõ ràng các =~
nhà điều hành , sau đó nó hoạt động trên $_
. Điều này để nói rằng đó s/foo/bar/
là viết tắt của $_ =~ s/foo/bar/
.
Việc chuyển một s///
biểu thức thành rename
đối số mã (nghĩa là đối số dòng lệnh đầu tiên) là điều phổ biến, nhưng bạn không phải làm vậy. Bạn có thể cung cấp cho nó bất kỳ mã Perl nào bạn muốn cho nó chạy bên trong vòng lặp, để kiểm tra từng giá trị $_
và (điều kiện) sửa đổi nó.
Điều này có rất nhiều hậu quả thú vị và hữu ích , nhưng phần lớn trong số chúng vượt xa phạm vi của câu hỏi và câu trả lời này. Lý do chính tôi đưa vấn đề này lên đây - thực tế, lý do chính khiến tôi quyết định đăng câu trả lời này - là để đưa ra quan điểm rằng, bởi vì đối số đầu tiên rename
thực sự là mã Perl, bất cứ khi nào bạn nhận được thông báo lỗi lạ và bạn gặp khó khăn khi tìm thông tin về nó bằng cách tìm kiếm, đôi khi bạn có thể thêm "Perl" vào chuỗi tìm kiếm (hoặc thậm chí thay thế "đổi tên" bằng "Perl" và đôi khi bạn sẽ tìm thấy câu trả lời.
2. Với rename *.DAT *.dat
, rename
lệnh không bao giờ thấy *.DAT
!
Một lệnh như rename s/foo/bar/ *.txt
thường không chuyển *.txt
như một đối số dòng lệnh cho rename
chương trình và bạn không muốn nó , trừ khi bạn có một tệp có tên theo nghĩa đen *.txt
, hy vọng bạn không làm như vậy.
rename
không giải thích mẫu glob thích *.txt
, *.DAT
, *.dat
, x*
, *y
, hoặc *
khi truyền cho nó như các đối số tên đường dẫn. Thay vào đó, shell của bạn thực hiện mở rộng tên đường dẫn trên chúng (còn được gọi là mở rộng tên tệp và còn được gọi là globalbing). Điều này xảy ra trước khi rename
tiện ích được chạy. Shell mở rộng các khối u thành nhiều tên đường tiềm năng và chuyển tất cả chúng, dưới dạng các đối số dòng lệnh riêng biệt, sang rename
. Trong Ubuntu, vỏ tương tác của bạn là Bash , trừ khi bạn đã thay đổi nó, đó là lý do tại sao tôi đã liên kết với hướng dẫn tham khảo Bash ở trên.
Có một tình huống trong đó một mẫu hình cầu có thể được chuyển qua dưới dạng một đối số dòng lệnh chưa được mở rộng thành rename
: khi nó không khớp với bất kỳ tệp nào. Các shell khác nhau thể hiện hành vi mặc định khác nhau trong tình huống này, nhưng hành vi mặc định của Bash chỉ đơn giản là vượt qua toàn cầu theo nghĩa đen. Tuy nhiên, bạn hiếm khi muốn điều này! Nếu bạn đã muốn nó, thì bạn nên đảm bảo rằng mô hình không được mở rộng, bằng cách trích dẫn nó. Điều này đi để truyền đối số cho bất kỳ lệnh nào, không chỉ để rename
.
Trích dẫn không chỉ dành cho toàn cầu hóa (mở rộng tên tệp), bởi vì có những mở rộng khác mà trình bao của bạn thực hiện trên văn bản không được trích dẫn và, đối với một số trong số chúng nhưng không phải trên văn bản được đặt trong "
"
dấu ngoặc kép. Nói chung, bất cứ khi nào bạn muốn vượt qua một đối số có chứa các ký tự có thể được xử lý đặc biệt bởi trình bao, bao gồm cả khoảng trắng, bạn nên trích dẫn nó, tốt nhất là bằng '
'
dấu ngoặc kép .
Mã Perl s/foo/bar/
không chứa bất cứ thứ gì được xử lý đặc biệt bởi shell, nhưng nó cũng là một ý tưởng tốt cho tôi để trích dẫn điều đó - và được viết 's/foo/bar/'
. (Trên thực tế, lý do duy nhất tôi không làm là nó gây nhầm lẫn cho một số độc giả, vì tôi chưa nói về việc trích dẫn.) Lý do tôi nói điều này sẽ tốt là vì rất phổ biến rằng mã Perl có chứa những ký tự như vậy và nếu tôi thay đổi mã đó, tôi có thể không nhớ kiểm tra xem có cần trích dẫn không. Ngược lại, nếu bạn muốn shell mở rộng toàn cầu, nó không được trích dẫn.
3. Trình thông dịch Perl nghĩa là gì bởi "bareword không được phép"
Các thông báo lỗi bạn hiển thị trong câu hỏi của bạn tiết lộ rằng, khi bạn chạy rename *.DAT *.dat
, trình bao của bạn mở rộng *.DAT
thành một danh sách một hoặc nhiều tên tệp và rằng tên tệp đầu tiên trong số đó là b1.DAT
. Tất cả các đối số tiếp theo - cả bất kỳ đối số nào khác được mở rộng từ *.DAT
và bất kỳ đối số nào được mở rộng từ *.dat
--came sau đối số đó, vì vậy chúng sẽ được hiểu là tên đường dẫn.
Bởi vì những gì thực sự chạy là một cái gì đó giống như rename b1.DAT ...
và vì rename
coi đối số không phải là tùy chọn đầu tiên của nó là mã Perl, câu hỏi trở thành: tại sao lại b1.DAT
tạo ra các lỗi "bareword không được phép" này khi bạn chạy nó dưới dạng mã 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).
Trong một shell, chúng tôi trích dẫn các chuỗi của chúng tôi để bảo vệ chúng khỏi các mở rộng shell ngoài ý muốn , nếu không chúng sẽ tự động chuyển chúng thành các chuỗi khác (xem phần bên trên). Shell là ngôn ngữ lập trình mục đích đặc biệt hoạt động rất khác với ngôn ngữ có mục đích chung (và cú pháp và ngữ nghĩa rất kỳ lạ của chúng phản ánh điều đó). Nhưng Perl là một ngôn ngữ lập trình có mục đích chung và, giống như hầu hết các ngôn ngữ lập trình có mục đích chung, mục đích chính của trích dẫn trong Perl không phải là để bảo vệ các chuỗi, mà là đề cập đến chúng. Đây thực sự là một cách mà hầu hết các ngôn ngữ lập trình tương tự như ngôn ngữ tự nhiên. Trong tiếng Anh, và giả sử bạn có một con chó, "con chó của bạn" là một cụm từ có hai từ, trong khi con chó của bạn là một con chó. Tương tự, trong Perl, '$foo'
là một chuỗi, trong khi đó $foo
là một cái gì đó có tên $foo
.
Tuy nhiên, không giống như mọi ngôn ngữ lập trình có mục đích chung khác, đôi khi Perl cũng sẽ hiểu văn bản không được trích dẫn là đề cập đến một chuỗi - một chuỗi "giống" như nó, theo nghĩa là nó được tạo thành từ cùng một ký tự trong cùng thứ tự. Nó sẽ chỉ cố gắng diễn giải mã theo cách đó nếu đó là một bareword (không $
hoặc sigil khác, xem bên dưới), và sau khi nó không thể tìm thấy bất kỳ ý nghĩa nào khác để cung cấp cho nó. Sau đó, nó sẽ coi nó như một chuỗi, trừ khi bạn nói với nó là không bằng cách kích hoạt các hạn chế .
Các biến trong Perl thường bắt đầu bằng một ký tự dấu chấm câu được gọi là sigil , chỉ định loại rộng của biến. Ví dụ, $
có nghĩa là vô hướng , @
có nghĩa là mảng và %
có nghĩa là hàm băm . ( Có những người khác. ) Đừng lo lắng nếu bạn thấy khó hiểu (hoặc nhàm chán), bởi vì tôi chỉ đưa ra để nói rằng khi một tên hợp lệ xuất hiện trong chương trình Perl nhưng không được đặt trước bởi một sigil, tên đó được cho là một bareword .
Barewords phục vụ các mục đích khác nhau, nhưng chúng thường biểu thị một hàm dựng sẵn hoặc một chương trình con do người dùng định nghĩa đã được xác định trong chương trình (hoặc trong một mô-đun được sử dụng bởi chương trình). Perl không có các hàm dựng sẵn được gọi là b1
hoặc DAT
, vì vậy khi trình thông dịch Perl nhìn thấy mã b1.DAT
, nó sẽ cố gắng xử lý b1
và DAT
như tên của các chương trình con. Giả sử không có chương trình con nào được xác định, điều này thất bại. Sau đó, các hạn chế được cung cấp chưa được kích hoạt, nó coi chúng là các chuỗi. Điều này sẽ hoạt động, mặc dù bạn có thực sự dự định điều này xảy ra hay không là phỏng đoán của bất kỳ ai. .
Toán tử Perl nối chuỗi , do đó, b1.DAT
đánh giá chuỗib1DAT
. Đó là, b1.DAT
một cách xấu để viết một cái gì đó như 'b1' . 'DAT'
hoặc "b1" . "DAT"
.
Bạn có thể tự kiểm tra điều này bằng cách chạy lệnh perl -E 'say b1.DAT'
, chuyển tập lệnh Perl ngắn say b1.DAT
cho trình thông dịch Perl, chạy nó, in b1DAT
. (Trong lệnh đó, '
'
dấu ngoặc kép nói vỏ để vượt qua say b1.DAT
như một đối số dòng lệnh đơn, nếu không, không gian sẽ gây ra say
và b1.DAT
được phân tích như lời riêng biệt và perl
sẽ nhận được chúng như các đối số riêng biệt. perl
Không không thấy có dấu ngoặc kép mình, như các vỏ loại bỏ chúng .)
Nhưng bây giờ, hãy thử viếtuse strict;
theo kịch bản Perl trước đây say
. Bây giờ nó không thành công với cùng một loại lỗi bạn nhận được từ 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.
Điều này xảy ra vì đã use strict;
cấm người phiên dịch Perl coi các barewords là chuỗi. Để cấm tính năng đặc biệt này, nó thực sự đủ để kích hoạt chỉ subs
giới hạn. Lệnh này tạo ra các lỗi tương tự như trên:
perl -E 'use strict "subs"; say b1.DAT'
Nhưng thông thường các lập trình viên Perl sẽ chỉ viết use strict;
, cho phép subs
hạn chế và hai người khác. use strict;
thường được khuyến khích thực hành. Vì vậy, rename
lệnh làm điều này cho mã của bạn . Đó là lý do tại sao bạn nhận được thông báo lỗi đó.
4. Tóm lại, đây là những gì đã xảy ra:
- Shell của bạn được truyền
b1.DAT
dưới dạng đối số dòng lệnh đầu tiên, rename
được coi là mã Perl để chạy trong một vòng lặp cho mỗi đối số tên đường dẫn.
- Điều này đã được thực hiện có nghĩa là
b1
và DAT
kết nối với các .
nhà điều hành.
b1
và DAT
không có tiền tố với sigils, vì vậy chúng được coi là barewords.
- Hai barewords đó sẽ được coi là tên của các hàm dựng sẵn hoặc bất kỳ chương trình con nào do người dùng định nghĩa, nhưng không có thứ nào có tên đó.
- Nếu "subs subs" không được kích hoạt, thì chúng sẽ được xử lý như các biểu thức chuỗi
'b1'
và 'DAT
'và được nối. Đó là xa những gì bạn dự định, cho thấy tính năng này thường không hữu ích.
- Tuy nhiên, "tàu ngầm nghiêm ngặt" được kích hoạt, bởi vì
rename
cho phép tất cả các hạn chế ( vars
, refs
và subs
). Do đó, bạn đã có một lỗi thay thế.
rename
bỏ do lỗi này Bởi vì loại lỗi này xảy ra sớm, không có nỗ lực đổi tên tệp nào được thực hiện, mặc dù bạn đã không vượt qua -n
. Đây là một điều tốt, thường bảo vệ người dùng khỏi những thay đổi tên tệp không chủ ý và đôi khi thậm chí khỏi mất dữ liệu thực tế.
Cảm ơn Zanna , người đã giúp tôi giải quyết một số thiếu sót quan trọng trong một bản thảo ealier của câu trả lời này . Không có cô ấy, câu trả lời này sẽ trở nên ít ý nghĩa hơn, và có thể không được đăng lên.