Tại sao tên thư mục của tôi lại kết thúc như thế này và làm cách nào để khắc phục điều này bằng cách sử dụng tập lệnh?


15

Xin lỗi nếu điều này có câu trả lời ở nơi khác, tôi không biết làm thế nào để tìm kiếm vấn đề của mình.

Tôi đã chạy một số mô phỏng trên máy chủ HPC linux, và mã của tôi để xử lý cấu trúc thư mục để lưu kết quả đầu ra có một lỗi đáng tiếc. Mã MATLAB của tôi để tạo thư mục là:

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

trong đó sp.run_numberlà một số nguyên. Tôi đã quên chuyển đổi nó thành một chuỗi, nhưng vì một số lý do, việc chạy mkdir(folder);(trong matlab) vẫn thành công. Trong thực tế, các mô phỏng đã chạy mà không gặp trở ngại nào và dữ liệu đã được lưu vào thư mục phù hợp.

Bây giờ, khi cấu trúc thư mục được truy vấn / in, tôi nhận được các tình huống sau:

  • Khi tôi cố gắng tự động hoàn thành tab: run_ run_^A/ run_^B/ run_^C/ run_^D/ run_^E/ run_^F/ run_^G/ run_^H/ run_^I/
  • Khi tôi sử dụng ls: run_ run_? run_? run_? run_? run_? run_? run_? run_? run_? run_?.
  • Khi tôi chuyển sang máy Mac của mình bằng rsync, --progresstùy chọn hiển thị: run_\#003/vv với (tôi giả sử) số khớp với số nguyên được sp.run_numberđệm thành ba chữ số, vì vậy lần chạy thứ 10 làrun_\#010/
  • Khi tôi xem các thư mục trong công cụ tìm, tôi thấy run_ run_ run_ run_ run_ run_ run_ run_ run_ run_?
  • Nhìn vào câu hỏi này và sử dụng lệnh ls | LC_ALL=C sed -n ltôi nhận được:
run_$
run_\001$
run_\002$
run_\003$
run_\004$
run_\005$
run_\006$
run_\a$
run_\b$
run_\t$
run_$

Tôi không thể quản lý cdvào các thư mục bằng cách sử dụng bất kỳ biểu diễn nào trong số này.

Tôi có hàng ngàn thư mục này, vì vậy tôi sẽ cần sửa nó bằng một tập lệnh. Lựa chọn nào trong số các tùy chọn này là đại diện chính xác của thư mục? Làm cách nào để tôi có thể tham khảo các thư mục này theo cách lập trình để tôi đổi tên chúng với tên được định dạng đúng bằng cách sử dụng tập lệnh bash? Và tôi đoán vì sự tò mò, làm thế nào trong địa ngục đã xảy ra ở nơi đầu tiên?


4
"Khi tôi cố gắng tự động hoàn thành tab: ... Nếu tôi cố gắng nhập ..." Tại sao phải nhập và không để tự động hoàn tất hoàn thành nếu dành cho bạn? Cũng ^Akhông ^theo nghĩa đen A, nhưng Ctrl-A (bạn có thể nhập nó bằng Ctrl-V Ctrl-A vì Ctrl-A thường là phím tắt cho trình bao).
muru

@muru không hoạt động ... Tôi đã đi xa run_và tôi phải gõ một cái gì đó
Phill

Xin lỗi đã nhận xét trước khi tôi thấy chỉnh sửa của bạn, quản lý để đưa tôi vào qua cd
Phill

Bản sao có thể có của Chọn tên tệp unicode trong Bash
muru

9
BTW, "một số lý do" tại sao mkdir trong matlab đã làm điều này là do CHỈ các ký tự không hợp lệ trong tên tệp hoặc thư mục trên các hệ thống tệp unix là NUL và dấu gạch chéo /. Bất kỳ ký tự nào khác là hợp lệ, bao gồm các ký tự điều khiển. Tôi không biết matlab sẽ làm gì nếu sp.run_number bằng 0 (có thể hủy bỏ với lỗi hoặc tạo ra run_, vì byte NUL sẽ chấm dứt chuỗi tên thư mục). Tất nhiên, điều này cũng có vấn đề đối với các giá trị 16 bit (hoặc cao hơn) có byte NUL trong đó và cũng sẽ thay đổi tùy theo mức độ cuối của hệ thống đang chạy MATLAB.
cas

Câu trả lời:


26

Bạn có thể sử dụng renametiện ích perl (aka prenamehoặc file-rename) để đổi tên các thư mục.

LƯU Ý: Điều này không được nhầm lẫn với renametừ util-linux, hoặc bất kỳ phiên bản nào khác.

rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/

Điều này sử dụng ord()chức năng của perl để thay thế từng ký tự điều khiển trong tên tệp bằng số thứ tự cho ký tự đó. ví dụ như ^Atrở thành 1, ^Btrở thành 2, v.v.

Các -ntùy chọn là một khô hạn để hiển thị những gì rename sẽ làm gì nếu bạn để cho nó. Loại bỏ nó (hoặc thay thế nó bằng -vcho đầu ra dài dòng) để thực sự đổi tên.

Công cụ esửa đổi trong s/LHS/RHS/eghoạt động khiến perl thực thi RHS (thay thế) dưới dạng mã perl và $1là dữ liệu trùng khớp (ký tự điều khiển) từ LHS.

Nếu bạn muốn số không đệm trong tên tệp, bạn có thể kết hợp ord()với sprintf(). ví dụ

$ rename -n 's/([[:cntrl:]])/sprintf("%02i",ord($1))/eg' run_*/ | sed -n l
rename(run_\001, run_01)$
rename(run_\002, run_02)$
rename(run_\003, run_03)$
rename(run_\004, run_04)$
rename(run_\005, run_05)$
rename(run_\006, run_06)$
rename(run_\a, run_07)$
rename(run_\b, run_08)$
rename(run_\t, run_09)$

Các ví dụ trên hoạt động khi và chỉ khi sp.run_number trong tập lệnh MATLAB của bạn nằm trong phạm vi 0,26 (vì vậy nó tạo ra các ký tự điều khiển trong tên thư mục).

Để xử lý BẤT K byte ký tự 1 byte nào (tức là từ 0..255), bạn sẽ sử dụng:

rename -n 's/run_(.)/sprintf("run_%03i",ord($1))/e' run_*/

Nếu sp.run_numbercó thể> 255, bạn phải sử dụng unpack()chức năng của perl thay vì ord(). Tôi không biết chính xác làm thế nào matlab tạo ra một int chưa được chuyển đổi trong một chuỗi, vì vậy bạn sẽ phải thử nghiệm. Xem perldoc -f unpackđể biết chi tiết.

ví dụ như sau đây sẽ giải nén cả hai giá trị không dấu 8 bit và 16 bit và không đệm chúng rộng đến 5 chữ số:

 rename -n 's/run_(.*)/sprintf("run_%05i",unpack("SC",$1))/e' run_*/

Cảm ơn các chi tiết! Tôi đang cố gắng thử nghiệm nó với -ntùy chọn, nhưng nó cho tôi biết đó là một tùy chọn không hợp lệ - thông tin phiên bản mang lại cho tôi rename from util-linux 2.23.2vì vậy tôi không chắc nó có chức năng tương tự
Phill

3
đó là lý do tại sao tôi chỉ định phiên bản perl của renametiện ích. util-linuxCủa renamerất khác nhau, ít khả năng hơn và các tùy chọn dòng lệnh không tương thích. nếu bạn đang chạy debian hoặc tương tự, hãy thử cài đặt file-renamegói. Nếu không thì cài đặt gói thích hợp cho bản phân phối của bạn. nó có thể đã được cài đặt, thử chạy prenamehoặc file-renamethay vì chỉ rename.
cas

Vâng tôi nghĩ đó là trường hợp. Tôi sẽ xem liệu tôi có thể khiến một trong số họ làm việc không. Cảm ơn một lần nữa vì đã dành thời gian để giúp tôi!
Phill

11

Và tôi đoán vì lợi ích của sự tò mò, làm thế nào trong cái quái này đã xảy ra ở nơi đầu tiên?

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

trong đó sp.run_numberlà một số nguyên. Tôi quên chuyển đổi nó thành một chuỗi, nhưng vì một số lý do chạy mkdir(folder); (trong matlab) vẫn thành công.

Vì vậy, có vẻ như mkdir([...])trong Matlab ghép các thành viên của mảng để xây dựng tên tệp dưới dạng chuỗi. Nhưng thay vào đó, bạn đã cho nó một con số và con số là những ký tự trên máy tính thực sự là gì. Vì vậy, khi sp.run_numberđược 1, nó đã cho bạn nhân vật có giá trị 1, và sau đó là nhân vật có giá trị 2, v.v.

Đó là những ký tự điều khiển, chúng không có ký hiệu có thể in được và in chúng trên thiết bị đầu cuối sẽ có những hậu quả khác. Vì vậy, thay vào đó, chúng thường được đại diện bởi các loại thoát khác nhau: \001(bát phân), \x01(hex), ^Ađều là các đại diện phổ biến cho nhân vật có giá trị 1. Ký tự có giá trị 0 khác một chút, đó là byte NUL được sử dụng để đánh dấu sự kết thúc của một chuỗi trong C và trong các cuộc gọi hệ thống Unix.

Nếu bạn đã đi cao hơn 31, bạn sẽ bắt đầu thấy các ký tự có thể in được, 32 là khoảng trắng (mặc dù không hiển thị lắm), 33 = !, 34 =, "v.v.

Vì thế,

  • run_ run_^A/ run_^B/- Cái đầu tiên run_tương ứng với cái có byte bằng 0, chuỗi kết thúc ở đó. Những cái khác cho thấy shell của bạn thích sử dụng hiển thị mã điều khiển với ^A. Ký hiệu cũng gợi ý rằng thực tế là char có giá trị bằng số 1 có thể được nhập vào Ctrl-A, mặc dù bạn cần nói với shell để diễn giải không phải là một ký tự điều khiển, nhưng như một nghĩa đen, Ctrl-V Ctrl-Anên làm điều đó ít nhất là trong Bash.

  • ls: run_ run_? run_?- lskhông thích in các ký tự không thể in trên thiết bị đầu cuối, nó thay thế chúng bằng các dấu hỏi.

  • rsync: run_\#003/- đó là một cái mới đối với tôi, nhưng ý tưởng là như nhau, dấu gạch chéo ngược đánh dấu một lối thoát và phần còn lại là giá trị số của ký tự. Dường như với tôi rằng số ở đây là số bát phân, giống như phổ biến hơn \003.

  • sử dụng lệnh ls | LC_ALL=C sed -n l... run_\006$ run_\a$ run_\b$ run_\t$- \a, \b\tlà C thoát cho báo động (chuông), backspace và tab, tương ứng. Chúng có các giá trị số 7, 8 và 9, vì vậy cần phải rõ ràng lý do tại sao chúng đến sau \006. Sử dụng các lối thoát C đó là một cách khác để đánh dấu các ký tự điều khiển. Các dấu hiệu đồng đô la đánh dấu kết thúc dòng.

Đối với cdgiả sử giả định của tôi là đúng, cd run_nên chuyển đến một thư mục duy nhất mà không có ký tự lẻ và cd run_?sẽ đưa ra lỗi vì dấu hỏi là ký tự toàn cục khớp với bất kỳ ký tự đơn nào và chỉ có nhiều tên tệp trùng khớp, nhưng cdchỉ mong đợi một.

Lựa chọn nào trong số các tùy chọn này là đại diện chính xác của thư mục?

Tất cả bọn họ, theo một nghĩa nào đó ...

Trong Bash, bạn có thể sử dụng \000\x00thoát bên trong $'...'dấu ngoặc kép để biểu thị các ký tự đặc biệt, vì vậy $'run_\033(bát phân) hoặc $'run_\x1b'tương ứng với thư mục có giá trị ký tự 27 (xảy ra là ESC). (Tôi không nghĩ Bash hỗ trợ thoát với số thập phân.)

Câu trả lời của cas có một kịch bản để đổi tên chúng, vì vậy tôi sẽ không đến đó.


Nếu là GNU ls, có một số tùy chọn trích dẫn bao gồm -b/ --escape--quoting-style=, hoặc QUOTING_STYLEbiến môi trường, để kiểm soát cách hiển thị các ký tự không in. Tuy nhiên, tôi không nghĩ rằng có một tùy chọn để làm cho nó thích thoát bát phân hơn các phiên bản nhân vật.
Toby Speight

3

Dễ nhất là tạo tên tệp sai và tên tệp chính xác trong cùng một môi trường xảy ra sự cố rủi ro, sau đó chỉ cần di chuyển / đổi tên các thư mục thành tên chính xác.

Để tránh xung đột giữa các tên hiện có, tốt hơn nên sử dụng thư mục đích khác.

./saveLocationA/wrongname1 -> ./saveLocationB/correctname1
./saveLocationA/wrongname2 -> ./saveLocationB/correctname2
./saveLocationA/wrongname3 -> ./saveLocationB/correctname3

Nếu có thể, tôi thích sửa tập lệnh hơn và chỉ chạy lại nó; sửa một số lỗi chết người kỳ lạ có thể tốn kém hơn và có thể giới thiệu các vấn đề mới.

Chúc may mắn!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.