Đây không phải là một vấn đề tầm thường. Shell thực hiện loại bỏ trích dẫn trước khi gọi hàm, vì vậy không có cách nào hàm có thể tạo lại các trích dẫn chính xác như bạn đã gõ chúng.
Tuy nhiên, nếu bạn chỉ muốn có thể in ra một chuỗi có thể được sao chép và dán để lặp lại lệnh, có hai cách tiếp cận khác nhau bạn có thể thực hiện:
- Xây dựng một chuỗi lệnh được chạy qua
eval
và chuyển chuỗi đó đếndry_run
- Trích dẫn các ký tự đặc biệt của lệnh
dry_run
trước khi in
Sử dụng eval
Đây là cách bạn có thể sử dụng eval
để in chính xác những gì đang chạy:
dry_run() {
printf '%s\n' "$1"
[ -z "${DRY_RUN}" ] || return 0
eval "$1"
}
email_admin() {
echo " Emailing admin"
dry_run 'su - '"$target_username"' -c "cd '"$GIT_WORK_TREE"' && git log -1 -p|mail -s '"'$mail_subject'"' '"$admin_email"'"'
echo " Emailed"
}
Đầu ra:
su - webuser1 -c "cd /home/webuser1/public_html && git log -1 -p|mail -s 'Git deployment on webuser1' user@domain.com"
Lưu ý số lượng trích dẫn điên rồ - bạn đã có một lệnh trong một lệnh trong một lệnh, điều này sẽ trở nên xấu đi nhanh chóng. Chú ý: Đoạn mã trên sẽ có vấn đề nếu các biến của bạn chứa khoảng trắng hoặc ký tự đặc biệt (như dấu ngoặc kép).
Trích dẫn nhân vật đặc biệt
Cách tiếp cận này cho phép bạn viết mã một cách tự nhiên hơn, nhưng đầu ra của con người khó đọc hơn vì cách thức nhanh và bẩn shell_quote
được thực hiện:
# This function prints each argument wrapped in single quotes
# (separated by spaces). Any single quotes embedded in the
# arguments are escaped.
#
shell_quote() {
# run in a subshell to protect the caller's environment
(
sep=''
for arg in "$@"; do
sqesc=$(printf '%s\n' "${arg}" | sed -e "s/'/'\\\\''/g")
printf '%s' "${sep}'${sqesc}'"
sep=' '
done
)
}
dry_run() {
printf '%s\n' "$(shell_quote "$@")"
[ -z "${DRY_RUN}" ] || return 0
"$@"
}
email_admin() {
echo " Emailing admin"
dry_run su - "${target_username}" -c "cd $GIT_WORK_TREE && git log -1 -p|mail -s '$mail_subject' $admin_email"
echo " Emailed"
}
Đầu ra:
'su' '-' 'webuser1' '-c' 'cd /home/webuser1/public_html && git log -1 -p|mail -s '\''Git deployment on webuser1'\'' user@domain.com'
Bạn có thể cải thiện khả năng đọc của đầu ra bằng cách thay đổi shell_quote
thành ký tự đặc biệt dấu gạch chéo ngược thay vì gói mọi thứ trong dấu ngoặc đơn, nhưng thật khó để làm chính xác.
Nếu bạn thực hiện shell_quote
phương pháp này, bạn có thể xây dựng lệnh để chuyển đến su
một cách an toàn hơn. Các mục sau sẽ hoạt động ngay cả khi ${GIT_WORK_TREE}
, ${mail_subject}
hoặc ${admin_email}
chứa các ký tự đặc biệt (dấu ngoặc đơn, dấu cách, dấu hoa thị, dấu chấm phẩy, v.v.):
email_admin() {
echo " Emailing admin"
cmd=$(
shell_quote cd "${GIT_WORK_TREE}"
printf '%s' ' && git log -1 -p | '
shell_quote mail -s "${mail_subject}" "${admin_email}"
)
dry_run su - "${target_username}" -c "${cmd}"
echo " Emailed"
}
Đầu ra:
'su' '-' 'webuser1' '-c' ''\''cd'\'' '\''/home/webuser1/public_html'\'' && git log -1 -p | '\''mail'\'' '\''-s'\'' '\''Git deployment on webuser1'\'' '\''user@domain.com'\'''