Hành vi của các lệnh tương tự (theo Bash) - chạy từng cái một trong bảng điều khiển so với tập lệnh đơn


1

Chạy trên OS X 10.11.1, tôi thực thi một loạt các lệnh sau (từng cái một), trong bảng điều khiển:

FILE="a b c.tiff"  # file in the current folder
VAR=$(mdls -name kMDItemContentCreationDate $FILE) # storing the creation time string  
TS=$(echo ${VAR[2]}; echo ${VAR[3]}  # saving the date and time only
echo $TS  

và việc mở rộng hoạt động rất đẹp. Đầu ra cho thấy:

2016-01-16 15:34:29

Tuy nhiên, khi tôi lưu những thứ này trong một tập lệnh và chạy nó, có vẻ như một cái gì đó trong quá trình đánh giá là khác nhau.

Mang lại (với bash -x ):

FILE='a b c.tiff'
mdls -name kMDItemContentCreationDate a b c.tiff
VAR='a: could not find a.'
echo
echo
TS=
echo

, vì vậy tôi thấy việc mở rộng hành xử khác nhau.

Mối quan tâm của tôi là tại sao sự khác biệt này xảy ra và tôi nên sửa kịch bản của mình như thế nào. Cảm ơn bạn.

Câu trả lời:


2

Tôi khá chắc chắn rằng bạn đã không chạy các lệnh chính xác đó trong bảng điều khiển hoặc bạn sẽ nhận được kết quả tương tự. Có hai vấn đề nghiêm trọng ở đây, và một vài thực hành kịch bản xấu. Vấn đề nghiêm trọng đầu tiên:

  • Khi bạn đề cập đến một biến (ví dụ $FILE) không có dấu ngoặc kép xung quanh nó, shell sẽ chia nó thành "từ" và sau đó mở rộng bất kỳ ký tự đại diện nào trước khi chuyển nó vào lệnh. Trong trường hợp này, điều đó có nghĩa là a b c.tiffsẽ được chia thành "a", "b" và "c.tiff". Đó là lý do tại sao bạn nhận được "a: không thể tìm thấy a." lỗi.

    Giải pháp: bạn nên đặt các tham chiếu biến trong dấu ngoặc kép trừ khi bạn đặc biệt muốn mở rộng phân tách từ và ký tự đại diện. (Có một số trường hợp bỏ dấu ngoặc kép là an toàn, nhưng theo dõi chúng thì rắc rối hơn giá trị của nó. Hãy tập thói quen sử dụng dấu ngoặc kép.)

  • Khi bạn sử dụng một phép gán như thế VAR=$(somecommand), nó sẽ gán biến dưới dạng một chuỗi đơn giản, không phải là một mảng. Để lưu trữ nó dưới dạng một mảng, sử dụng dấu ngoặc đơn ở bên phải, như trong VAR=( $(somecommand) ). Lưu ý rằng vì $(somecommand)không nằm trong dấu ngoặc kép, nên nó sẽ được chia từ và mở rộng ký tự đại diện, nhưng trong trường hợp này, chúng tôi muốn chia tách từ (vì vậy mỗi "từ" được lưu trữ trong một thành phần mảng riêng) và định dạng đầu ra đủ có thể dự đoán được rằng việc mở rộng ký tự đại diện sẽ không làm điều gì kỳ lạ để làm chúng tôi khó chịu. Vì vậy, đây là một trong những trường hợp hiếm hoi khi bỏ dấu ngoặc kép là ok.

Với hai cái cố định này, dòng thứ hai trở thành:

VAR=( $(mdls -name kMDItemContentCreationDate "$FILE") )

Bây giờ, đối với một số điều là thói quen viết kịch bản xấu không thực sự gây rắc rối ở đây:

  • Trong bài tập TS=$(echo ${VAR[2]}; echo ${VAR[3]})(lưu ý: Tôi đã thêm dấu ngoặc đơn bị thiếu), lệnh thay thế và echocác lệnh không làm gì hữu ích. Những gì nó làm là lấy các giá trị phần tử mảng, phân chia từ và ký tự đại diện mở rộng chúng (không có gì ở đây), chuyển chúng làm tham số cho echocác lệnh, lấy đầu ra của các lệnh đó và thu thập nó thành một biến. Đó là rất nhiều công việc để gắn hai chuỗi với nhau. Chỉ cần sử dụng `TS =" $ {VAR [2]} $ {VAR [3]} ".

    BTW, nó cũng đang làm một điều gì đó hơi kỳ lạ: đó là gắn các chuỗi với nhau bằng một dòng mới giữa chúng. Khi bạn in nó echo $TS, nó (một lần nữa) sẽ được phân chia từ, do đó, mỗi dòng được coi là một đối số riêng biệt echo, sẽ gắn khoảng trắng giữa các đối số. Kết quả cuối cùng: echolệnh đang chuyển đổi hiệu quả dòng mới thành không gian. Sẽ tốt hơn nhiều nếu biến nó thành một khoảng trống ở vị trí đầu tiên, và sau đó trích dẫn hai lần khi bạn sử dụng nó, vì vậy tập lệnh của bạn không phụ thuộc vào hai bit hành vi kỳ lạ xảy ra để triệt tiêu lẫn nhau.

  • Cuối cùng, sử dụng biến all-caps không an toàn lắm. Có một số biến all-caps có ý nghĩa đặc biệt đối với shell (và một số lệnh) và nếu bạn vô tình sử dụng một trong số chúng, bạn có thể nhận được kết quả kỳ lạ. Ví dụ kinh điển là gán một cái gì đó cho PATH, tại thời điểm đó, tất cả các lệnh đột nhiên không được nhận dạng. Thật khó để theo dõi tất cả các biến ma thuật, vì vậy chỉ cần sử dụng các biến chữ thường cho nội dung của bạn và bạn sẽ an toàn.

Với tất cả điều này đã được làm sạch, đây là những gì tôi nhận được cho kịch bản:

file="a b c.tiff"  # file in the current folder
var=( $(mdls -name kMDItemContentCreationDate "$file") ) # storing the creation time string as an array
ts="${var[2]} ${var[3]}"  # saving the date and time only
echo "$ts"

Lưu ý cuối cùng: khi nghi ngờ, hãy chạy tập lệnh của bạn thông qua shellcheck.net - nó sẽ chỉ ra nhiều lỗi bắt đầu chuẩn và giúp bạn tiết kiệm rất nhiều thời gian!


Cảm ơn bạn. Tôi tự hỏi liệu dán từ Notes.app có bất kỳ loại tác dụng phụ nào trong hành vi tôi đang thấy không. Ví dụ: ngay cả khi sử dụng dấu ngoặc kép quanh $ FILE cũng không hoạt động trong tập lệnh để mở rộng nội dung của nó, mà thay vào đó là trích dẫn quảng cáo (đã thử cách này trước khi đăng). Tôi sẽ cần kiểm tra lại xem chúng là "trích dẫn thông minh" hay không. Tôi sẽ một lần nữa cảm ơn bạn vì những lời giải thích rõ ràng, cũng như những lời khuyên về thực hành tốt; vô giá
người cao tuổi

@elderelder Vâng, hầu hết các biên tập viên sẽ làm những việc "hữu ích" (như thay thế trích dẫn) làm rối các tập lệnh. TextWrangler là một lựa chọn tốt hơn cho kịch bản, bởi vì nó khá giống với những gì bạn nói với nó.
Gordon Davisson

Tôi đánh giá cao đề nghị.
người cao tuổi
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.