Mở rộng các biến trong các dấu ngoặc đơn trong một lệnh trong Bash


393

Tôi muốn chạy một lệnh từ một tập lệnh bash có các dấu ngoặc đơn và một số lệnh khác bên trong dấu ngoặc đơn và một biến.

ví dụ repo forall -c '....$variable'

Trong định dạng này, $được thoát và biến không được mở rộng.

Tôi đã thử các biến thể sau đây nhưng chúng đã bị từ chối:

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

Nếu tôi thay thế giá trị thay cho biến, lệnh sẽ được thực thi tốt.

Xin vui lòng cho tôi biết tôi đang đi sai ở đâu.


33
repo forall -c ' ...before... '"$variable"' ...after...'
n. 'đại từ' m.

2
@nm các dấu ngoặc đơn là một phần của lệnh repo. tôi không nghĩ rằng nó sẽ hoạt động
Rachit

1
bashăn dấu ngoặc đơn. Hoặc bạn không ở trong bash, hoặc dấu ngoặc đơn không phải là một phần của repolệnh.
n. 'đại từ' m.

Không chắc chắn tôi có được bạn không, nhưng trong trường hợp các trích dẫn đơn lẻ cần phải có, sẽ không repo forall -c "'... trước ..." $ biến "... sau khi ...'" hoạt động?
ecv

Đó là một tập lệnh bash chạy trong bash shell và lệnh repo forall lấy các tham số bên trong các dấu ngoặc đơn. Nếu tôi thay thế giá trị thực, lệnh sẽ chạy tốt.
Rachit

Câu trả lời:


621

Bên trong trích dẫn duy nhất mọi thứ được bảo tồn theo nghĩa đen, không có ngoại lệ.

Điều đó có nghĩa là bạn phải đóng dấu ngoặc kép, chèn một cái gì đó và sau đó nhập lại.

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

Word concatenation được thực hiện đơn giản bởi juxtap vị trí. Như bạn có thể xác minh, mỗi dòng trên là một từ duy nhất cho trình bao. Trích dẫn (trích dẫn đơn hoặc kép, tùy theo tình huống) không cô lập từ. Chúng chỉ được sử dụng để vô hiệu hóa việc giải thích các ký tự đặc biệt khác nhau, như khoảng trắng $, ;... Để có hướng dẫn tốt về trích dẫn, hãy xem câu trả lời của Mark Reed. Cũng có liên quan: Những nhân vật cần phải được trốn thoát trong bash?

Không nối các chuỗi được giải thích bởi một trình bao

Bạn tuyệt đối nên tránh xây dựng các lệnh shell bằng cách ghép các biến. Đây là một ý tưởng tồi tương tự như ghép các đoạn SQL (SQL SQL!).

Thông thường có thể có các trình giữ chỗ trong lệnh và cung cấp lệnh cùng với các biến để callee có thể nhận chúng từ danh sách đối số gọi.

Ví dụ, những điều sau đây là rất không an toàn. ĐỪNG LÀM ĐIỀU NÀY

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

Nếu nội dung $myvarkhông đáng tin cậy, đây là một khai thác:

myvar='foo"; echo "you were hacked'

Thay vì lời gọi trên, hãy sử dụng các đối số vị trí. Yêu cầu sau là tốt hơn - không thể khai thác:

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

Lưu ý việc sử dụng các dấu tick đơn lẻ trong bài tập script, có nghĩa là nó được thực hiện theo nghĩa đen, không có sự mở rộng biến đổi hoặc bất kỳ hình thức giải thích nào khác.


@ Jo.SO sẽ không hoạt động. bởi vì lệnh repo sẽ chỉ lấy các tham số cho đến khi các dấu ngoặc kép đầu tiên được đóng lại. Bất cứ điều gì sau đó sẽ bị bỏ qua cho repo và tạo ra một lỗi khác.
Rạch

8
@Rachit - shell không hoạt động như vậy. "some"thing' like'thislà tất cả một từ để vỏ. Dấu ngoặc kép không chấm dứt bất cứ điều gì liên quan đến phân tích cú pháp và không có cách nào để một lệnh được gọi bởi shell để cho biết những gì được trích dẫn như thế nào.
Mark Reed

3
Câu thứ hai đó ("bạn thậm chí không thể thoát khỏi dấu ngoặc đơn") tạo ra dấu ngoặc đơn cho các lỗ đen của Shell.

@Evert: Không biết làm thế nào để nói nó tốt hơn. Tôi đã xóa câu.
Jo So

@JoSo Đó là một sự xấu hổ: không có trò đùa rơi xuống.

96

Các repolệnh không thể chăm sóc những loại dấu ngoặc kép nó được. Nếu bạn cần mở rộng tham số, sử dụng dấu ngoặc kép. Nếu điều đó có nghĩa là bạn sẽ phải viết lại nhiều nội dung, hãy sử dụng các trích dẫn duy nhất cho phần lớn nội dung đó, sau đó thoát ra khỏi chúng và tăng gấp đôi cho phần mà bạn cần mở rộng.

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

Giải thích sau, nếu bạn quan tâm.

Khi bạn chạy một lệnh từ shell, thứ mà lệnh đó nhận làm đối số là một chuỗi các chuỗi kết thúc null. Những chuỗi đó có thể chứa hoàn toàn bất kỳ ký tự không null nào.

Nhưng khi shell đang xây dựng chuỗi các chuỗi từ một dòng lệnh, nó sẽ diễn giải một số ký tự đặc biệt; điều này được thiết kế để làm cho các lệnh dễ dàng hơn (thực sự, có thể) để gõ. Chẳng hạn, khoảng trắng thường chỉ ra ranh giới giữa các chuỗi trong mảng; vì lý do đó, các đối số riêng lẻ đôi khi được gọi là "từ". Nhưng một cuộc tranh luận dù sao cũng có thể có khoảng trống trong đó; bạn chỉ cần một số cách để nói với cái vỏ đó là những gì bạn muốn.

Bạn có thể sử dụng dấu gạch chéo ngược phía trước bất kỳ ký tự nào (bao gồm dấu cách hoặc dấu gạch chéo ngược khác) để nói với shell để xử lý ký tự đó theo nghĩa đen. Nhưng trong khi bạn có thể làm một cái gì đó như thế này:

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

... Nó có thể gây mệt mỏi. Vì vậy, vỏ cung cấp một thay thế: dấu ngoặc kép. Chúng có hai loại chính.

Dấu ngoặc kép được gọi là "dấu ngoặc kép". Chúng ngăn các ký tự đại diện và bí danh được mở rộng, nhưng chủ yếu là để bao gồm các khoảng trắng trong một từ. Những điều khác như mở rộng tham số và lệnh (các loại điều được báo hiệu bởi a $) vẫn xảy ra. Và tất nhiên nếu bạn muốn có một trích dẫn kép theo nghĩa đen bên trong dấu ngoặc kép, bạn phải gạch chéo lại nó:

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

Dấu ngoặc đơn là hà khắc hơn. Tất cả mọi thứ giữa chúng được thực hiện hoàn toàn theo nghĩa đen, bao gồm cả dấu gạch chéo ngược. Hoàn toàn không có cách nào để có được một trích dẫn theo nghĩa đen bên trong các trích dẫn đơn.

May mắn thay, dấu ngoặc kép trong vỏ không phải là dấu phân cách từ ; bởi chính họ, họ không chấm dứt một từ. Bạn có thể vào và ra dấu ngoặc kép, bao gồm giữa các loại trích dẫn khác nhau, trong cùng một từ để có kết quả mong muốn:

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

Vì vậy, điều đó dễ dàng hơn - ít dấu gạch chéo ngược hơn, mặc dù chuỗi trích dẫn đơn, trích dẫn ngược theo nghĩa đen, trích dẫn đơn, trích dẫn đơn đã làm quen với một số.

Các shell hiện đại đã thêm một kiểu trích dẫn khác không được chỉ định bởi tiêu chuẩn POSIX, trong đó dấu ngoặc kép đơn hàng đầu được thêm tiền tố bằng ký hiệu đô la. Các chuỗi được trích dẫn tuân theo các quy ước tương tự với các chuỗi ký tự trong ngôn ngữ lập trình ANSI C, và do đó đôi khi được gọi là "chuỗi ANSI" và $'... 'cặp "trích dẫn ANSI". Trong các chuỗi như vậy, lời khuyên ở trên về dấu gạch chéo ngược được thực hiện theo nghĩa đen không còn được áp dụng. Thay vào đó, chúng trở nên đặc biệt một lần nữa - bạn không chỉ có thể bao gồm một dấu ngoặc kép đơn hoặc dấu gạch chéo ngược bằng cách thêm dấu gạch chéo ngược vào nó, mà trình bao còn mở rộng ký tự ANSI C thoát ra (như \ncho một dòng mới, \tcho tab và \xHHcho ký tự mã thập lục phânHH). Mặt khác, tuy nhiên, chúng hoạt động như các chuỗi trích dẫn đơn: không có thay thế tham số hoặc lệnh:

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'

Điều quan trọng cần lưu ý là chuỗi đơn nhận được làm đối số cho echolệnh hoàn toàn giống nhau trong tất cả các ví dụ này. Sau khi shell được phân tích cú pháp một dòng lệnh, không có cách nào để lệnh được chạy để cho biết những gì đã được trích dẫn như thế nào. Ngay cả khi nó muốn.


2
Đúng. Giờ thì tôi đã hiểu. Nó hoạt động hoàn hảo. Cảm ơn sự giúp đỡ của bạn!
Rạch

6
Câu trả lời này thật tuyệt vời.
Vinícius Ferrão

1
$'string'định dạng POSIX compliant? Ngoài ra, có một tên cho nó?
tự đại diện

2
@Wildcard $'string'là một tiện ích mở rộng không phải POSIX, mà tôi đã thấy được gọi là "chuỗi ANSI"; Tôi đã kết hợp những sự thật đó vào câu trả lời. Các chuỗi như vậy hoạt động trong hầu hết các shell tương thích Bourne hiện đại: bash, dash, ksh (cả AT & T và PD) và zsh đều hỗ trợ chúng.
Mark Reed

1
Liên kết lại - Những ký tự nào được yêu cầu để thoát trong các đối số dòng lệnh? trên sàn giao dịch ngăn xếp Unix & Linux.
tự đại diện

3

Dưới đây là những gì làm việc cho tôi -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

2
Tôi hy vọng TBL_HDFS_DIR_PATH không phải là người dùng cung cấp đầu vào btw, điều này sẽ cho phép tiêm sql đẹp ...
domenukk

1
Đưa QUOTEvào một biến riêng biệt là hoàn toàn thừa; dấu ngoặc đơn bên trong dấu ngoặc kép chỉ là một ký tự thông thường. (Ngoài ra, không sử dụng chữ hoa cho các biến riêng tư của bạn.)
tripleee 23/03/19

2

EDIT: (Theo các ý kiến ​​trong câu hỏi :)

Tôi đã xem xét điều này kể từ đó. Tôi đã rất may mắn khi tôi có repo nằm xung quanh. Vẫn chưa rõ ràng cho tôi liệu bạn có cần phải đóng lệnh giữa các dấu ngoặc đơn bằng vũ lực hay không. Tôi đã xem xét cú pháp repo và tôi không nghĩ bạn cần phải làm vậy. Bạn có thể sử dụng dấu ngoặc kép xung quanh lệnh của mình, và sau đó sử dụng bất kỳ dấu ngoặc đơn và dấu ngoặc kép nào bạn cần bên trong với điều kiện bạn thoát khỏi dấu ngoặc kép.


Cảm ơn Kevin vì sự giúp đỡ của bạn.
Rạch

2

chỉ cần sử dụng printf

thay vì

repo forall -c '....$variable'

sử dụng printf để thay thế mã thông báo biến bằng biến mở rộng.

Ví dụ:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")

Cái này hỏng rồi; printfkhông thực sự mua cho bạn bất cứ điều gì ở đây và việc không trích dẫn điều khoản trợ cấp lệnh gây ra các vấn đề trích dẫn mới.
tripleee 23/03/19

0

Các biến có thể chứa các trích dẫn đơn.

myvar=\'....$variable\'

repo forall -c $myvar

Họ có thể, nhưng đây không phải là cách đúng đắn để làm điều đó - bạn vẫn nên đặt "$myvar"dấu ngoặc kép.
tripleee 23/03/19

-2

công việc này là dành cho bạn?

eval repo forall -c '....$variable'

8
Nó cho bạn? Câu hỏi này năm tuổi và đã có một số câu trả lời, thậm chí là một câu được chấp nhận ...
Nico Haase
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.