tl; dr
Một duy nhất stuffsẽ có lẽ hầu hết việc cho bạn.
Câu trả lời đầy đủ
Chuyện gì xảy ra
Khi bạn chạy foo $(stuff), đây là những gì xảy ra:
stuff chạy;
- đầu ra của nó (stdout), thay vì được in, thay thế
$(stuff)trong lời gọi của foo;
- sau đó
foochạy, đối số dòng lệnh của nó rõ ràng phụ thuộc vào những gì được stufftrả về.
Đây $(…)cơ chế được gọi là "lệnh thay". Trong trường hợp của bạn, lệnh chính là echovề cơ bản in các đối số dòng lệnh của nó thành thiết bị xuất chuẩn. Vì vậy, bất cứ điều gì stuffcố gắng in ra thiết bị xuất chuẩn đều được ghi lại, chuyển đến echovà in ra thiết bị xuất chuẩn echo.
Nếu bạn muốn đầu ra của stuffđược in ra thiết bị xuất chuẩn, chỉ cần chạy đế stuff.
Các `…`cú pháp phục vụ cùng một mục đích như $(…)(dưới cái tên giống nhau: "lệnh thay"), có vài sự khác biệt mặc dù, vì vậy bạn không thể nhắm mắt trao đổi chúng. Xem Câu hỏi thường gặp này và câu hỏi này .
Tôi có nên tránh echo $(stuff)không có vấn đề gì?
Có một lý do bạn có thể muốn sử dụng echo $(stuff)nếu bạn biết những gì bạn đang làm. Vì lý do tương tự, bạn nên tránh echo $(stuff)nếu bạn không thực sự biết những gì bạn đang làm.
Điểm này là stuffvà echo $(stuff)không chính xác tương đương. Cái sau có nghĩa là gọi toán tử split + global trên đầu ra stuffvới giá trị mặc định là $IFS. Trích dẫn kép thay thế lệnh ngăn chặn điều này. Trích dẫn đơn thay thế lệnh làm cho nó không còn là một thay thế lệnh.
Để quan sát điều này khi chia tách, hãy chạy các lệnh sau:
echo "a b"
echo $(echo "a b")
echo "$(echo "a b")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a b")'
Và cho toàn cầu:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
Như bạn có thể thấy echo "$(stuff)"là tương đương (-ish *) với stuff. Bạn có thể sử dụng nó nhưng điểm phức tạp của mọi thứ theo cách này là gì?
Mặt khác, nếu bạn muốn đầu ra stufftrải qua quá trình phân tách + tạo khối thì bạn có thể thấy echo $(stuff)hữu ích. Nó phải là quyết định có ý thức của bạn mặc dù.
Có các lệnh tạo đầu ra nên được đánh giá (bao gồm chia tách, tạo khối và nhiều hơn nữa) và được chạy bởi trình bao, do đó eval "$(stuff)"là một khả năng (xem câu trả lời này ). Tôi chưa bao giờ thấy một lệnh nào cần đầu ra của nó để trải qua quá trình phân tách + tạo khối bổ sung trước khi được in . Cố tình sử dụng echo $(stuff)có vẻ rất không phổ biến.
Thế còn var=$(stuff); echo "$var"?
Điểm tốt. Đoạn trích này:
var=$(stuff)
echo "$var"
nên tương đương với echo "$(stuff)"tương đương (-ish *) với stuff. Nếu đó là toàn bộ mã, chỉ cần chạy stuffthay thế.
Tuy nhiên, nếu bạn cần sử dụng đầu ra của stuffnhiều lần thì phương pháp này
var=$(stuff)
foo "$var"
bar "$var"
thường tốt hơn
foo "$(stuff)"
bar "$(stuff)"
Thậm chí nếu foolà echovà bạn nhận được echo "$var"trong mã của bạn, nó có thể là tốt hơn để giữ nó theo cách này. Những điều cần cân nhắc:
- Với
var=$(stuff) stuffchạy một lần; ngay cả khi lệnh nhanh, tránh tính toán cùng một đầu ra hai lần là điều đúng đắn. Hoặc có thể stuffcó các hiệu ứng khác ngoài việc ghi vào thiết bị xuất chuẩn (ví dụ: tạo tệp tạm thời, khởi động dịch vụ, khởi động máy ảo, thông báo cho máy chủ từ xa), vì vậy bạn không muốn chạy nó nhiều lần.
- Nếu
stufftạo đầu ra phụ thuộc thời gian hoặc hơi ngẫu nhiên, bạn có thể nhận được kết quả không nhất quán từ foo "$(stuff)"và bar "$(stuff)". Sau khi var=$(stuff)giá trị của $varđược cố định và bạn có thể chắc chắn foo "$var"và bar "$var"nhận được đối số dòng lệnh giống hệt nhau.
Trong một số trường hợp thay vì foo "$var"bạn có thể muốn (cần) sử dụng foo $var, đặc biệt nếu stufftạo nhiều đối số cho foo(một biến mảng có thể tốt hơn nếu shell của bạn hỗ trợ nó). Một lần nữa, biết những gì bạn đang làm. Khi nói đến echosự khác biệt giữa echo $varvà echo "$var"giống như giữa echo $(stuff)và echo "$(stuff)".
* Tương đương ( -ish )?
Tôi nói echo "$(stuff)"là tương đương (-ish) với stuff. Có ít nhất hai vấn đề khiến nó không tương đương chính xác:
$(stuff)chạy stufftrong một subshell, vì vậy tốt hơn echo "$(stuff)"là nói tương đương (-ish) với (stuff). Các lệnh ảnh hưởng đến lớp vỏ mà chúng chạy trong, nếu trong lớp con, không ảnh hưởng đến lớp vỏ chính.
Trong ví dụ stuffnày là a=1; echo "$a":
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
So sánh nó với
a=0
a=1; echo "$a" # stuff
echo "$a"
và với
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
Một ví dụ khác, bắt đầu với stuffviệc cd /; pwd:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
và thử nghiệm stuffvà (stuff)các phiên bản.
echokhông phải là một công cụ tốt để hiển thị dữ liệu không được kiểm soát . Điều này echo "$var"chúng ta đã nói về lẽ ra phải có printf '%s\n' "$var". Nhưng vì câu hỏi đề cập echovà vì giải pháp có thể xảy ra nhất là không sử dụng echongay từ đầu, nên tôi quyết định không giới thiệu printfcho đến bây giờ.
stuffhoặc (stuff)sẽ xen kẽ đầu ra stdout và stderr, trong khi echo $(stuff)sẽ in tất cả đầu ra stderr từ stuff(chạy trước) và chỉ sau đó đầu ra stdout được tiêu hóa bởi echo(chạy cuối cùng).
$(…)loại bỏ bất kỳ dòng mới nào và sau đó echothêm nó trở lại. Vì vậy, echo "$(printf %s 'a')" | xxdcho đầu ra khác nhau hơn printf %s 'a' | xxd.
Một số lệnh ( lsví dụ) hoạt động khác nhau tùy thuộc vào việc đầu ra tiêu chuẩn có phải là bàn điều khiển hay không; vì thế ls | catkhông giống nhau lskhông. Tương tự echo $(ls)sẽ làm việc khác hơn ls.
Đặt lssang một bên, trong trường hợp chung nếu bạn phải ép buộc hành vi khác này thì stuff | cattốt hơn echo $(ls)hoặc echo "$(ls)"bởi vì nó không kích hoạt tất cả các vấn đề khác được đề cập ở đây.
Có thể trạng thái thoát khác nhau (được đề cập cho tính đầy đủ của câu trả lời wiki này; để biết chi tiết, hãy xem câu trả lời khác xứng đáng với tín dụng).