Назад | Перейти на главную страницу

Вызов rsync с аргументами, хранящимися в строке

Это тот же сценарий, что и приведенный в моем потоке mhddfs, я бы хотел определить аргументы в соответствии с некоторыми входными данными и впоследствии вызвать rasync. В настоящее время я придумал это, но не работает только вызов rsync:

#!/bin/bash

INCLUDE=""
EXCLUDE=""
USER=""
HOST=""
KEY=""
FORCE=false
SHUTDOWN=false
DESTINATION=""
LASTFULL=""

while getopts u:h:k:s:o:f:e:i: option
do
    case "${option}" in
        u) USER=${OPTARG};;
        i) INCLUDE=${OPTARG};;
        e) EXCLUDE=${OPTARG};;
        h) HOST=${OPTARG};;
        k) KEY=${OPTARG};;
        s) SHUTDOWN=true;;
        o) DESTINATION=${OPTARG};;
        f) FORCE=true;
    esac
done

if [ -z $USER ] || [ -z $HOST ] || [ -z $DESTINATION ]
then
    echo "Usage : $0 <-u : user> <-h : host> <-t : type> <-o : output directory> [-k : key] [-s : shutdown]"
    exit 1
fi

if [ ! -d "$DESTINATION/../Backup" ]
then
    if [ ! -d "$DESTINATION/Backup" ]
    then
        echo "Creating backup directory on target..."

        if ! mkdir "$DESTINATION/Backup"
        then
            echo "Could not create the backup directory. Exiting."
            exit 2
        fi
    fi
    DESTINATION="$DESTINATION/Backup"
fi

RSYNC_ARGS="-ravh -H --rsync-path=\"sudo rsync\" --append --progress --delete --safe-links"
if [ -f "$KEY" ]
then
    RSYNC_ARGS="$RSYNC_ARGS -e \"ssh -i $KEY\""
fi
if [ -f "$INCLUDE" ]
then
    echo "Including only files from $INCLUDE"
    RSYNC_ARGS="$RSYNC_ARGS --files-from=$INCLUDE"
fi
if [ -f "$EXCLUDE" ]
then
    echo "Excluding files from $EXCLUDE"
    RSYNC_ARGS="$RSYNC_ARGS --exclude-from=$EXCLUDE"
fi
if ! $FORCE
then
    LASTBACKUP=$(basename $(ls -td  $DESTINATION/*/ | sed "y/\t/\n/" | head -n 1 ) 2>/dev/null)
    echo "Processing backup based on --> $LASTBACKUP"
    RSYNC_ARGS="$RSYNC_ARGS --link-dest=../$LASTBACKUP"
else
    echo "Processing full backup"
fi

DESTINATION=$(echo "$DESTINATION/$(date '+%Y_%m_%d')" | sed "s#//#/#g")

rsync $RSYNC_ARGS $USER@$HOST:/ $DESTINATION 2> error.log

CODE=$?
if [ $CODE -eq 0 ]
then
    echo "Backup done : $DESTINATION"
    exit 0
else
    echo "Rsync stopped with code $CODE"
    exit $CODE
fi

я получил

Rsync stopped with code 1

error.log содержит

Unexpected remote arg : <correct username>@<correct host>:/

Когда я повторяю вызов rsync, копирую и вставляю его, он работает

Я знаю, что $ RSYNC_ARGS все портит, но не знаю, как это правильно назвать. Любая идея ?

Оболочка анализирует кавычки перед раскрытием переменных, поэтому помещение кавычек в значение переменной не дает того, что вы ожидаете - к тому времени, когда они будут на месте, им уже слишком поздно делать что-нибудь полезное. Видеть BashFAQ # 50: Я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу! Больше подробностей.

В таких случаях, когда вы хотите динамически создавать список аргументов команды, лучшим вариантом является сохранение списка аргументов в массиве. Таким образом, кавычки анализируются при создании массива, каждое «слово» сохраняется как отдельный элемент массива, и если вы правильно ссылаетесь на переменную, элементы массива включаются в список аргументов команды без какого-либо нежелательного анализа:

rsync_args=(-ravh -H --rsync-path="sudo rsync" --append --progress --delete --safe-links)
if [ -f "$key" ]
then
    rsync_args+=(-e "ssh -i $key")
fi
if [ -f "$include" ]
then
    echo "Including only files from $include"
    rsync_args+=(--files-from="$include")
fi
# ... etc
rsync "${rsync_args[@]}" "$user@$host:/" "$destination" 2> error.log

Обратите внимание, что все круглые скобки и += в заданиях; и двойные кавычки, фигурные скобки и [@] при использовании массива обязательный чтобы это работало правильно.

Кстати, вы можете заметить, что я использовал все имена переменных в нижнем регистре. Использование имен переменных в верхнем регистре в оболочке опасно, потому что существует ряд из них, имеющих особое значение для оболочки и / или других программ, и случайное повторное использование одной из них может иметь непредвиденные последствия. По факту, $USER и $HOST такие (они инициализируются текущим локальным именем пользователя и именем хоста); повторно использовать их наверное не вызовет проблем, но лучше просто избежать проблемы, используя имена переменных в нижнем или смешанном регистре. Но вы можете добавить user=$USER в начале скрипта, поэтому по умолчанию будет использоваться то же имя пользователя, если -u не указано.

Кроме того, я заключил в двойные кавычки ссылки на переменные, у которых их не было. Вы часто можете обойтись без них, но лучше использовать их, чтобы избежать потенциальных проблем. Я рекомендую запустить ваши скрипты через shellcheck.net, так как он помогает выявлять такие типичные проблемы.

Я не мог публиковать сообщения, пока не прошло 40 минут с момента моего последнего сообщения. Тем временем я использовал

eval rsync $RSYNC_ARGS $USER@$HOST:/ $DESTINATION 2> error.log

Что отлично работает.