Xử lý nhiều cấp độ trích dẫn (thực sự, nhiều cấp độ phân tích / giải thích) có thể trở nên phức tạp. Nó giúp ghi nhớ một vài điều:
- Mỗi cấp độ trích dẫn của Wap có thể liên quan đến một ngôn ngữ khác nhau.
- Quy tắc trích dẫn thay đổi theo ngôn ngữ.
- Khi giao dịch với nhiều hơn một hoặc hai cấp độ lồng nhau, thường thì dễ dàng nhất để làm việc từ trên xuống, lên trên (nghĩa là trong cùng đến ngoài cùng).
Cấp độ trích dẫn
Hãy để chúng tôi xem xét các lệnh ví dụ của bạn.
pgrep -fl java | grep -i datanode | awk '{print $1}'
Lệnh ví dụ đầu tiên của bạn (ở trên) sử dụng bốn ngôn ngữ: shell của bạn, regex trong pgrep , regex trong grep (có thể khác với ngôn ngữ regex trong pgrep ) và awk . Có hai cấp độ diễn giải liên quan: shell và một cấp độ sau shell cho mỗi lệnh liên quan. Chỉ có một mức trích dẫn rõ ràng (trích dẫn shell vào awk ).
ssh host …
Tiếp theo bạn đã thêm một mức ssh trên đầu trang. Đây thực sự là một mức vỏ khác: ssh không tự giải thích lệnh, nó trao nó cho một vỏ ở đầu xa (thông qua (ví dụ) sh -c …
) và shell đó diễn giải chuỗi.
ssh host "sudo su user -c …"
Sau đó, bạn hỏi về việc thêm một mức vỏ khác ở giữa bằng cách sử dụng su (thông qua sudo , không giải thích các đối số lệnh của nó, vì vậy chúng ta có thể bỏ qua nó). Tại thời điểm này, bạn có ba cấp độ lồng nhau đang diễn ra ( awk → shell, shell → shell ( ssh ), shell → shell ( su user -c ), vì vậy tôi khuyên bạn nên sử dụng phương pháp đáy, lên phương pháp Up. Tôi sẽ giả sử rằng shell của bạn tương thích với Bourne (ví dụ sh , ash , dash , ksh , bash , zsh , v.v.). Một số loại shell khác ( cá , RC , v.v.) có thể yêu cầu cú pháp khác nhau, nhưng phương pháp vẫn được áp dụng.
Từ dưới lên
- Xây dựng chuỗi bạn muốn đại diện ở cấp độ trong cùng.
- Chọn một cơ chế trích dẫn từ các tiết mục trích dẫn của ngôn ngữ cao nhất tiếp theo.
- Trích dẫn chuỗi mong muốn theo cơ chế trích dẫn đã chọn của bạn.
- Thường có nhiều biến thể để áp dụng cơ chế trích dẫn nào. Làm bằng tay thường là một vấn đề thực hành và kinh nghiệm. Khi thực hiện theo chương trình, thường là tốt nhất để chọn cách dễ nhất để có được quyền (thường là người giỏi nhất theo nghĩa đen (ít thoát nhất)).
- Tùy chọn, sử dụng chuỗi trích dẫn kết quả với mã bổ sung.
- Nếu bạn chưa đạt được mức trích dẫn / giải thích mong muốn của mình, hãy lấy chuỗi trích dẫn kết quả (cộng với bất kỳ mã được thêm nào) và sử dụng nó làm chuỗi bắt đầu trong bước 2.
Trích dẫn ngữ nghĩa khác nhau
Điều cần lưu ý ở đây là mỗi ngôn ngữ (mức trích dẫn) có thể đưa ra các ngữ nghĩa hơi khác nhau (hoặc thậm chí khác nhau về ngữ nghĩa) cho cùng một ký tự trích dẫn.
Hầu hết các ngôn ngữ đều có cơ chế trích dẫn chữ viết của người Viking, nhưng chúng khác nhau về chính xác nghĩa đen của chúng. Trích dẫn duy nhất của shell giống như Bourne thực sự là nghĩa đen (có nghĩa là bạn không thể sử dụng nó để trích dẫn một ký tự trích dẫn duy nhất). Ngôn ngữ khác (Perl, Ruby) ít chữ trong đó họ giải thích một số chuỗi xuyệc ngược bên trong vùng trích dẫn đơn không theo nghĩa đen (đặc biệt, \\
và \'
kết quả trong \
và '
, nhưng trình tự backslash khác đang thực sự theo nghĩa đen).
Bạn sẽ phải đọc tài liệu cho từng ngôn ngữ của bạn để hiểu các quy tắc trích dẫn và cú pháp tổng thể.
Ví dụ của bạn
Cấp độ trong cùng của ví dụ của bạn là một chương trình awk .
{print $1}
Bạn sẽ nhúng cái này vào dòng lệnh shell:
pgrep -fl java | grep -i datanode | awk …
Chúng ta cần bảo vệ (tối thiểu) không gian và $
trong chương trình awk . Sự lựa chọn rõ ràng là sử dụng trích dẫn duy nhất trong shell xung quanh toàn bộ chương trình.
Có những lựa chọn khác:
{print\ \$1}
trực tiếp thoát khỏi không gian và $
{print' $'1}
trích dẫn duy nhất không gian và $
"{print \$1}"
nhân đôi toàn bộ và thoát khỏi $
{print" $"1}
trích dẫn kép chỉ khoảng trắng và $
Điều này có thể uốn cong các quy tắc một chút (không thoát ra $
ở cuối chuỗi trích dẫn kép là bằng chữ), nhưng nó dường như hoạt động trong hầu hết các shell.
Nếu chương trình sử dụng dấu phẩy giữa dấu ngoặc nhọn mở và đóng, chúng ta cũng cần trích dẫn hoặc thoát dấu phẩy hoặc dấu ngoặc nhọn để tránh mở rộng niềng răng trong một số vỏ.
Chúng tôi chọn '{print $1}'
và nhúng nó vào phần còn lại của shell mã Code:
pgrep -fl java | grep -i datanode | awk '{print $1}'
Tiếp theo, bạn muốn chạy cái này qua su và sudo .
sudo su user -c …
su user -c …
cũng giống như some-shell -c …
(ngoại trừ chạy theo một số UID khác), vì vậy su chỉ cần thêm một mức vỏ khác. sudo không diễn giải các đối số của nó, vì vậy nó không thêm bất kỳ mức trích dẫn nào.
Chúng ta cần một mức vỏ khác cho chuỗi lệnh của chúng ta. Chúng tôi có thể chọn trích dẫn một lần nữa, nhưng chúng tôi phải xử lý đặc biệt cho các trích dẫn đơn hiện có. Cách thông thường trông như thế này:
'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Có bốn chuỗi ở đây mà shell sẽ diễn giải và ghép lại: chuỗi trích dẫn đơn đầu tiên ( pgrep … awk
), một trích dẫn đơn thoát, chương trình awk trích dẫn đơn , một trích dẫn đơn thoát khác.
Tất nhiên, có nhiều lựa chọn thay thế:
pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1}
thoát khỏi mọi thứ quan trọng
pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1}
giống nhau, nhưng không có khoảng trắng thừa (ngay cả trong chương trình awk !)
"pgrep -fl java | grep -i datanode | awk '{print \$1}'"
báo giá gấp đôi toàn bộ, thoát khỏi $
'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'"
biến thể của bạn; dài hơn một chút so với cách thông thường do sử dụng dấu ngoặc kép (hai ký tự) thay vì thoát (một ký tự)
Sử dụng trích dẫn khác nhau ở cấp độ đầu tiên cho phép các biến thể khác ở cấp độ này:
'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
'pgrep -fl java | grep -i datanode | awk {print\ \$1}'
Nhúng biến thể đầu tiên trong dòng lệnh sudo / * su * đưa ra điều này:
sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Bạn có thể sử dụng cùng một chuỗi trong bất kỳ bối cảnh cấp vỏ đơn nào khác (ví dụ ssh host …
).
Tiếp theo, bạn đã thêm một mức ssh trên đầu trang. Đây thực sự là một mức vỏ khác: ssh không tự giải thích lệnh, nhưng nó trao nó cho một vỏ ở đầu từ xa (thông qua (ví dụ) sh -c …
) và shell đó diễn giải chuỗi.
ssh host …
Quá trình này giống nhau: lấy chuỗi, chọn phương thức trích dẫn, sử dụng nó, nhúng nó.
Sử dụng dấu ngoặc đơn một lần nữa:
'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Bây giờ có mười một chuỗi được giải thích và nối: 'sudo su user -c '
, thoát dấu nháy đơn, 'pgrep … awk '
, thoát dấu nháy đơn, dấu chéo ngược trốn thoát, hai thoát dấu nháy đơn, các trích dẫn đơn awk chương trình, một thoát quote duy nhất, một dấu chéo ngược trốn thoát, và một thức thoát khỏi dấu nháy đơn .
Mẫu cuối cùng trông như thế này:
ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Điều này hơi khó sử dụng để gõ bằng tay, nhưng tính chất nghĩa đen của trích dẫn đơn của shell giúp dễ dàng tự động hóa một biến thể nhỏ:
#!/bin/sh
sq() { # single quote for Bourne shell evaluation
# Change ' to '\'' and wrap in single quotes.
# If original starts/ends with a single quote, creates useless
# (but harmless) '' at beginning/end of result.
printf '%s\n' "$*" | sed -e "s/'/'\\\\''/g" -e 1s/^/\'/ -e \$s/\$/\'/
}
# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }
ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"
ssh host "$(sq "$s2")"