我一直在努力解决以下问题,我编写了脚本,其中任何命令运行都首先放入字符串中,然后执行。这样,命令只写一次,就可以多次分析、显示和使用。它在大多数情况下都能正常工作,但在以下详细情况下则不行。所以如果你们能帮忙,我会非常高兴 ;) 我必须承认,在处理包含引号和空格的变量时,我一直在努力理解脚本的工作方式,所以如果你知道一个好的教程...
案例 1:mkdir 命令
dir="/tmp/Mytests/DirWith2 spaces 2"
cmdToRun="mkdir -p ${dir}"
echo "Running [${cmdToRun}]"
${cmdToRun} # <= this does not work, OK it seems normal !
cmdToRun="mkdir -p """${dir}""""
${cmdToRun} # <= this does not work either !
cmdToRun="mkdir -p \"${dir}\""
${cmdToRun} # <= this does not work either !
案例2:rsync命令
rsync_cmd="rsync -avz --stats "
rsync_destinationBaseDir="/tmp/ToTestRsync"
ListOfDirToSynchronize=( \
"/opt/dir1/subdir1" "/opt/dir1space 1/subdir1" "/opt/dir1space 1/subdir1space 1" )
rsync_host_from="root@SRV1:"
A loop on the directories in my ListOfDirToSynchronize
{
SourceDir="${ListOfDirToSynchronize[$i]}"
( # here we start a subshell to run // rsyncsto speed-up the whole process
cmdToRun="${rsync_cmd} ${rsync_host_from}'${SourceDir}/' ${rsync_destinationBaseDir}${SourceDir}"
funcLog " | INFO | Directories synchronisation | SubprocessID [${subProcessID}] | running command [${cmdToRun}]"
${cmdToRun} >> ${LOG_FILE} 2>&1 #<== this does not work when spaces in the directories
) # end of subshell
} # end of the loop
答案1
简短回答:参见Bash常见问题解答 #050。
长答案:通常,最好的方法是将命令放在数组中,而不是简单的变量中(eval 是不是推荐——它往往会产生新的和危险的错误)。以下是使用数组样式完成的情况 1:
dir="/tmp/Mytests/DirWith2 spaces 2"
cmdToRun=(mkdir -p "${dir}") # Note double-quotes to preserve spaces within $dir
"${cmdToRun[@]}" # This is the standard idiom for expanding an array preserving both spaces and word breaks
案例 2:
rsync_cmd=(rsync -avz --stats)
rsync_destinationBaseDir="/tmp/ToTestRsync"
ListOfDirToSynchronize=( \
"/opt/dir1/subdir1" "/opt/dir1space 1/subdir1" "/opt/dir1space 1/subdir1space 1" )
rsync_host_from="root@SRV1:"
# A loop on the directories in my ListOfDirToSynchronize
for SourceDir in "${ListOfDirToSynchronize[@]}"; do
( # here we start a subshell to run // rsyncsto speed-up the whole process
cmdToRun=("${rsync_cmd[@]}" "${rsync_host_from}${SourceDir}/" "${rsync_destinationBaseDir}${SourceDir}")
funcLog " | INFO | Directories synchronisation | SubprocessID [${subProcessID}] | running command [${cmdToRun[*]}]"
"${cmdToRun[@]}" >> ${LOG_FILE} 2>&1 #<== this does not work when spaces in the directories
) # end of subshell
done # end of the loop
请注意,在日志条目中我使用了${cmdToRun[*]}
而不是${cmdToRun[@]}
,因此它会用空格分隔数组条目,而不是将它们视为 funcLog 的单独参数。这意味着日志是不明确的(您无法区分文件名内的空格和文件名之间的空格);如果这是一个问题,请改用$(printf " %q" "${cmdToRun[@]}")
,它会为文件名内的空格添加引号/转义符/等。
答案2
该eval
命令是你的朋友:
dir="/tmp/Mytests/DirWith2 spaces 2"
cmdToRun="mkdir -p \"${dir}\""
eval "${cmdToRun}"
和:
cmdToRun="${rsync_cmd} ${rsync_host_from}'${SourceDir}/' '${rsync_destinationBaseDir}${SourceDir}'"
funcLog ...
eval "${cmdToRun}" >> "${LOG_FILE}" 2>&1