Bash 脚本:间接运行命令时处理空格

Bash 脚本:间接运行命令时处理空格

我一直在努力解决以下问题,我编写了脚本,其中任何命令运行都首先放入字符串中,然后执行。这样,命令只写一次,就可以多次分析、显示和使用。它在大多数情况下都能正常工作,但在以下详细情况下则不行。所以如果你们能帮忙,我会非常高兴 ;) 我必须承认,在处理包含引号和空格的变量时,我一直在努力理解脚本的工作方式,所以如果你知道一个好的教程...

案例 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

相关内容