3

Некоторое время я боролся со следующими проблемами, я написал сценарии, в которых любая запускаемая команда сначала помещается в строку, а затем выполняется. Таким образом, команда записывается только один раз, и ее можно анализировать, отображать, использовать несколько раз. Он работает нормально большую часть времени, но не в следующих подробных случаях ниже. Так что если кто-то из вас может помочь, я был бы более чем счастлив;) Я должен признать, что мне всегда трудно понять, как работает скрипт, когда он работает с переменными, содержащими кавычки и пробелы, так что если вы знаете хороший урок ...

Случай 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

2 ответа2

2

Краткий ответ: см. BashFAQ # 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[@]}") , и он добавит кавычки /escape-символы /etc для пробелов в именах файлов.

1

Команда 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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .