Я пытаюсь программно использовать rsync, и все хорошо, пока в путях нет пробелов. Затем я должен указать путь в сценарии, что также нормально, пока это не является необязательным.
В этом случае --link-dest может быть необязательным, и я пробовал варианты, чтобы приспособить его в обоих случаях, когда он есть, и нет, но у меня возникают проблемы, когда пути нужно заключать в кавычки.
Одним из вариантов было бы использовать оператор case и просто вызвать rsync с двумя разными строками, но есть и другие параметры rsync, которые могут быть или не быть, и комбинации могут быстро складываться, поэтому лучшее решение было бы неплохо. Поскольку это моя машина, и никто, кроме меня, не имеет к ней доступа, я не беспокоюсь о безопасности, поэтому, если eval или что-то в этом роде — единственный способ, это нормально.
#!/bin/bash
src='/home/x/ll ll'
d1='/home/x/rsy nc/a'
d2='/home/x/rsy nc/b'
rsync -a "$src" "$d1"
# Try 1
x1='--link-dest='
x2='/home/x/rsy nc/a'
rsync -a $x1"$x2" "$src" "$d2"
Это работает до тех пор, пока у вас не будет --link-dest, который будет выглядеть так:
x1=''
x2=''
rsync -a $x1"$x2" "$src" "$d2"
И rsync принимает пустой "" в качестве источника, поэтому он терпит неудачу.
# Try 2
x1='--link-dest = "'
x2='/home/x/rsy nc/a"'
rsync -a $x1$x2 "$src" "$d2"
Это не удается, потому что двойные кавычки в x1 и x2 берутся как часть пути, и поэтому создается ошибка «путь не найден», но, конечно, это работает следующим образом:
x1=''
x2=''
rsync -a $x1$x2 "$src" "$d2"
Я пробовал несколько других вариантов, но все они возвращаются к двум вышеперечисленным проблемам.
Вы можете использовать массивы Bash для решения такого рода проблем.
# inputs come from wherever
src='/home/x/ll ll'
d1='/home/x/rsy nc/a'
d2='/home/x/rsy nc/b'
x2='something'
# or
x2=''
# build the command
my_cmd=(rsync -a)
if [[ $x2 ]]; then
my_cmd+=("--link-dest=$x2")
fi
my_cmd+=("$src" "$d2")
# run the command
"${my_cmd[@]}"
Сначала я строю команду по крупицам, тщательно используя кавычки, чтобы сохранить параметры, состоящие из нескольких слов, как таковые, сохраняя каждый параметр/аргумент как элемент в моем массиве, включая саму команду rsync
.
Затем я вызываю его со странным синтаксисом "${my_cmd[@]}"
, что означает расширение всех элементов массива без повторного разделения элементов, состоящих из нескольких слов. Обратите внимание, что кавычки важны: если вы удалите их, несколько значений слов в массиве снова разделятся, и вы вернетесь к исходной точке.
Синтаксис "${my_cmd[*]}"
тоже существует, но он объединяет весь массив в одно слово, опять же, что вы хотите. Вам нужен [@]
, чтобы каждый элемент массива расширялся как отдельное слово в результате.
Google «массивы bash» для более подробной информации.
Редактировать: условные каналы
Если вы хотите, чтобы выходные данные вызывали какую-то другую команду при каком-то условии, вы можете просто передать ее в оператор if. Это работает:
"${my_cmd[@]}" |
if [[ $log_file]]; then
tee "$log_file"
fi
и направляет вывод в $log_file
, если эта переменная определена и не пуста.
Это сработало отлично. Я не видел, как также использовать технику для дополнительного добавления " | tee $log_file" или добавления "2>$error_file", что было бы бонусом, но это не нужно делать, мне просто было любопытно, поэтому дал Жарко.
@Reg Я точно не знаю, но я думаю, что перенаправления и каналы не будут работать в массиве, они должны быть вне массива. Если вам нужно запустить некоторые тесты, чтобы подтвердить, но попробуйте и посмотрите, что произойдет.
Да, это был вывод, к которому я пришел после того, как попробовал, что не проблема, мне просто было любопытно, поэтому я попробовал.
Но если вам нужны условные каналы, вы можете передать что-то в оператор if, хотя я только что проверил, и это работает нормально.
@Reg Я только что добавил пример кода для условной передачи вывода в тройник.
Вы можете хранить параметры в массиве (как в ответе Джоаниса) или использовать условное расширение (см. мой ответ здесь и раздел 3 BashFAQ # 50).