tl; dr
Một duy nhất stuff
sẽ 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 đó
foo
chạy, đối số dòng lệnh của nó rõ ràng phụ thuộc vào những gì được stuff
trả 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à echo
về 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ì stuff
cố gắng in ra thiết bị xuất chuẩn đều được ghi lại, chuyển đến echo
và 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à stuff
và 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 stuff
vớ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 stuff
trả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 stuff
thay thế.
Tuy nhiên, nếu bạn cần sử dụng đầu ra của stuff
nhiề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 foo
là echo
và 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)
stuff
chạ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ể stuff
có 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
stuff
tạ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 stuff
tạ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 echo
sự khác biệt giữa echo $var
và 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 stuff
trong 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ụ stuff
nà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 stuff
việc cd /; pwd
:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
và thử nghiệm stuff
và (stuff)
các phiên bản.
echo
khô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 echo
và vì giải pháp có thể xảy ra nhất là không sử dụng echo
ngay từ đầu, nên tôi quyết định không giới thiệu printf
cho đến bây giờ.
stuff
hoặ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 đó echo
thêm nó trở lại. Vì vậy, echo "$(printf %s 'a')" | xxd
cho đầu ra khác nhau hơn printf %s 'a' | xxd
.
Một số lệnh ( ls
ví 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 | cat
không giống nhau ls
không. Tương tự echo $(ls)
sẽ làm việc khác hơn ls
.
Đặt ls
sang 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 | cat
tố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).