Вопрос или проблема
Почему эта команда rsync работает, когда я ввожу ее буквально, но не работает, когда я создаю ее из переменных?
Вот переменные — сначала опции, которые я передаю в rsync в виде массива:
$ echo "${options[@]}"
-av --prune-empty-dirs -f "- *.flac" -f "- *.WMA" -f "- *.wma" -f "- *.ogg" -f "- *.mp4" -f "- *.m4a" -f "- *.webm" -f "- *.wav" -f "- *.ape" -f "- *.zip" -f "- *.rar"
$ echo ${options[6]}
-f
$ echo ${options[7]}
"- *.wma"
Затем исходная директория, из которой rsync должен скопировать файлы:
$ echo "\"$dir/\""
"/media/test/Ahmad Jamal Trio/Live at the Pershing/"
И целевая директория, в которую rsync должен скопировать файлы:
$ echo "\"$target_dir\""
"/home/test/mp3/Ahmad Jamal Trio/Live at the Pershing/"
Собираем все вместе:
$ echo "${options[@]}" "\"$dir/\"" "\"$target_dir\""
-av --prune-empty-dirs -f "- *.flac" -f "- *.WMA" -f "- *.wma" -f "- *.ogg" -f "- *.mp4" -f "- *.m4a" -f "- *.webm" -f "- *.wav" -f "- *.ape" -f "- *.zip" -f "- *.rar" "/media/test/Ahmad Jamal Trio/Live at the Pershing//" "/home/test/mp3/Ahmad Jamal Trio/Live at the Pershing/"
Все выглядит так, как должно. И действительно, это работает, если вы вводите команду буквально, как это:
$ rsync -av --prune-empty-dirs -f "- *.flac" -f "- *.WMA" -f "- *.wma" -f "- *.ogg" -f "- *.mp4" -f "- *.m4a" -f "- *.webm" -f "- *.wav" -f "- *.ape" -f "- *.zip" -f "- *.rar" "/media/test/Ahmad Jamal Trio/Live at the Pershing/" "/home/test/mp3/Ahmad Jamal Trio/Live at the Pershing/"
./
Ahmad Jamal Trio - Live at the Pershing - 01 - But Not for Me.mp3
Ahmad Jamal Trio - Live at the Pershing - 02 - Surrey With The Fringe On Top.mp3
Ahmad Jamal Trio - Live at the Pershing - 03 - Moonlight In Vermont.mp3
Ahmad Jamal Trio - Live at the Pershing - 04 - Music, Music, Music.mp3
Ahmad Jamal Trio - Live at the Pershing - 05 - No Greater Love.mp3
Ahmad Jamal Trio - Live at the Pershing - 06 - Poinciana.mp3
Ahmad Jamal Trio - Live at the Pershing - 07 - Wood'yn You.mp3
Ahmad Jamal Trio - Live at the Pershing - 08 - What's New.mp3
AlbumArtSmall.jpg
AlbumArtLarge.jpg
Folder.jpg
sent 43,194,376 bytes received 285 bytes 28,796,440.67 bytes/sec
total size is 43,182,454 speedup is 1.00
Но это не работает, когда я вызываю rsync, используя переменные в качестве аргументов:
$ rsync "${options[@]}" "\"$dir/\"" "\"$target_dir\""
Unknown filter rule: `"- *.flac"'
rsync error: syntax or usage error (code 1) at exclude.c(902) [client=3.1.2]
Часть ваших фильтров rsync
и исходные и целевые директории заключены в кавычки с дополнительными экранированными кавычками.
Удалите экранированные кавычки, и это должно сработать:
options=(
-av --prune-empty-dirs
-f "- *.flac"
-f "- *.WMA"
-f "- *.wma"
-f "- *.ogg"
-f "- *.mp4"
-f "- *.m4a"
-f "- *.webm"
-f "- *.wav"
-f "- *.ape"
-f "- *.zip"
-f "- *.rar"
)
dir="/media/test/Ahmad Jamal Trio/Live at the Pershing"
target_dir="/home/test/mp3/Ahmad Jamal Trio/Live at the Pershing"
rsync "${options[@]}" "$dir/" "$target_dir"
Я убрал завершающие косые черты из переменных dir
и target_dir
, /
уже добавляется к $dir
в вызове rsync
.
Другой способ — использовать модификаторы расширения переменных оболочки bash. Не уверен, что они существуют во всех оболочках?
Эти модификаторы: @U @l @E @Q @K @k @A @a @P
, смотрите руководство по bash или sh для получения дополнительной информации. Нам здесь нужен @E
например, для rsync — который, как я обнаружил, является очень требовательным
#!/bin/bash
trap "set +x +v" RETURN
set +x +v +e
RSYNC=/home/user/.local/bin/rsync
declare -a OPS
OPS=()
OPS+=(-rtpogAXlHDU -cvzxsh)
OPS+=("--rsync-path=${RSYNC}")
OPS+=(--filter '+r /mnt/sde/')
OPS+=(--filter '- *~')
SRC="https://unix.stackexchange.com/questions/550449/[email protected]:work'
DST='/mnt/sde/backup'
clear
echo ${RSYNC} "${OPS[@]@E}" "${SRC@E}" "${DST@E}"
echo -e "-----\n\n"
set -v -x
${RSYNC} "${OPS[@]@E}" "${SRC@E}" "${DST@E}"
В приведенном выше примере также включен вывод отладки, чтобы вы могли увидеть, что происходит.
Важные моменты:
- Способ добавления элементов массива с помощью
+=
может сделать код компактным, так как пробелы являются разделителями между элементами (как в первом примере) - Токен «весь массив»
[@]
расширяет весь массив в аргументы. - Имейте в виду, что альтернативный
[*]
ведет себя по-другому, он соединяет список через пробелы, а не как отдельные аргументы. - Существует подвох, так как обработчик синтаксического анализа командной строки в некоторых программах может спутать их, и иногда приходится использовать «неправильный».
- Расширение в двойных кавычках
"${...}"
гарантирует, что пробелы в аргументах не разделятся после расширения (мое общее правило: начинайте с двойного заключения в кавычки каждого расширения переменной bash) - Модификаторы
@E
в конце${...@E}
сообщают bash о необходимости экранировать любые символы во время расширения, которые в противном случае изменили бы вывод. (в этом случае одиночные кавычки и пробелы), хотя это также должно работать для таких расширенных символов, как юникод. Это значит, что они передаются как часть аргумента в правильной форме, а не удаляются или интерпретируются bash. - Вне темы: другие модификаторы выполняют такие действия, как изменение регистра, расширение в синтаксис или попытка защитить строки от атак внедрения.
Ответ или решение
Проблема, с которой вы столкнулись при использовании rsync
с аргументами фильтра, переданными в виде массива в bash, связана с некорректной обработкой кавычек внутри элементов массива. Давайте разберем это пошагово и представим рекомендации по решению проблемы.
Проблема
Когда вы передаете параметры и пути в rsync
через массивы, расширение этих параметров может не работать должным образом, если кавычки были использованы некорректно. Ваша проблема заключается в следующем:
-
Избыточные кавычки: Вы добавляли лишние экранированные кавычки, которые интерпретировались как часть строки, вместо того чтобы влиять на способ интерпретации содержимого.
-
Расширение массива: Использование неправильного метода расширения массива может приводить к тому, что элементы не интерпретируются как отдельные аргументы.
Решение
Для решения этой проблемы следуйте следующим шагам:
-
Удалите лишние кавычки: Убедитесь, что используете кавычки только там, где это необходимо для группы аргументов, но не в самих элементах массива.
-
Правильное расширение массива: Используйте
"${array[@]}"
для корректного расширения элементов массива, чтобы они интерпретировались как отдельные аргументы. -
Использование модификатора @E: Этот модификатор позволяет bash экранировать специальные символы, предотвращая их необычное поведение при передаче в
rsync
.
Пример исправленного кода
options=(
-av --prune-empty-dirs
-f "- *.flac"
-f "- *.WMA"
-f "- *.wma"
-f "- *.ogg"
-f "- *.mp4"
-f "- *.m4a"
-f "- *.webm"
-f "- *.wav"
-f "- *.ape"
-f "- *.zip"
-f "- *.rar"
)
dir="/media/test/Ahmad Jamal Trio/Live at the Pershing"
target_dir="/home/test/mp3/Ahmad Jamal Trio/Live at the Pershing"
rsync "${options[@]}" "$dir/" "$target_dir"
Заключение
При работе с массивами в bash важно помнить о специфике интерпретации кавычек и расширения массивов. Обеспечение корректного использования кавычек и методов расширения предотвращает ошибки синтаксиса и использования. Обратите внимание на оптимальное использование двойных кавычек и модификаторов, таких как @E
, для избежания непредсказуемого поведения. Это гарантирует, что команды выполняются точно так, как вы ожидаете, что особенно важно в командной строке и при работе с утилитами, такими как rsync
.
SEO Оптимизация
Использование правильных терминов и нюансов bash программирования помогает не только устранить ошибки, но и повысить узнаваемость вашего контента среди других IT специалистов, ищущих решение подобных проблем. Подключение описательных названий и тегов к проблеме может помочь вашей статье быть видимой и полезной в различных контекстах.
Таким образом, ваше внимание к деталям в решении проблемы с rsync
через bash демонстрирует высокую квалификацию и практическое понимание сложных технических задач.