这不是一个小问题。Shell会在调用函数之前执行引号删除操作,因此函数无法完全按照键入的内容重新创建引号。
但是,如果只希望能够打印出可以复制并粘贴以重复该命令的字符串,则可以采用两种不同的方法:
- 构建一个要运行的命令字符串
eval
,并将该字符串传递给dry_run
dry_run
在打印之前用引号括住命令的特殊字符
使用 eval
这是您可以eval
用来精确打印运行内容的方法:
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"
}
输出:
su - webuser1 -c "cd /home/webuser1/public_html && git log -1 -p|mail -s 'Git deployment on webuser1' user@domain.com"
请注意引号的疯狂程度–您在命令内的命令中有一个命令,但很快就会变得丑陋。注意:如果您的变量包含空格或特殊字符(如引号),则上面的代码会出现问题。
引用特殊字符
这种方法使您可以更自然地编写代码,但是由于实现了快捷方法,因此输出对于人类来说更难阅读shell_quote
:
# 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"
}
输出:
'su' '-' 'webuser1' '-c' 'cd /home/webuser1/public_html && git log -1 -p|mail -s '\''Git deployment on webuser1'\'' user@domain.com'
您可以通过更改shell_quote
为反斜杠转义特殊字符而不是将所有内容都括在单引号中来提高输出的可读性,但这很难正确完成。
如果执行此shell_quote
方法,则可以构造命令以su
更安全的方式传递给该命令。即使下面会的工作${GIT_WORK_TREE}
,${mail_subject}
或${admin_email}
包含特殊字符(单引号,空格,星号,分号等):
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"
}
输出:
'su' '-' 'webuser1' '-c' ''\''cd'\'' '\''/home/webuser1/public_html'\'' && git log -1 -p | '\''mail'\'' '\''-s'\'' '\''Git deployment on webuser1'\'' '\''user@domain.com'\'''