2016-02-15 2 views
2

Я пытаюсь вытащить список файлов через ssh с помощью rsync, но я не могу заставить его работать с именами файлов, на которых есть пробелы! Один пример файла:Shell: rsync неправильно обрабатывает пробелы в имени файла/пути

/home/pi/Transmission_Downloads/FUNDAMENTOS_JAVA_E_ORIENTAÇÃO_A_OBJETOS/2. Fundamentos da linguagem/estruturas-de-controle-if-else-if-e-else-v1.mp4 

и я пытаюсь передать его, используя этот код оболочки.

cat $file_name | while read LINE 
do 
    echo $LINE 
    rsync -aP "[email protected]$server:$LINE" $local_folder 
done 

и ошибка, я получаю это:

receiving incremental file list 
rsync: link_stat "/home/pi/Transmission_Downloads/FUNDAMENTOS_JAVA_E_ORIENTAÇÃO_A_OBJETOS/2." failed: No such file or directory (2) 
rsync: link_stat "/home/pi/Fundamentos" failed: No such file or directory (2) 
rsync: link_stat "/home/pi/da" failed: No such file or directory (2) 
rsync: change_dir "/home/pi//linguagem" failed: No such file or directory (2) 
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1655) [Receiver=3.1.0] 

Я не понимаю, почему это печать OK на экране, но разбирает имя файла/путь неправильно! Я знаю, что пробелы на самом деле являются обратными слэшами с пробелами, но не знают, как это решить. Sed (найти/заменить) не помогло, и я также попробовал этот код без успеха

while IFS='' read -r line || [[ -n "$line" ]]; do 
    echo "Text read from file: $line" 
    rsync -aP "[email protected]$server:$line" $local_folder 
done < $file_name 

Что я должен сделать, чтобы исправить это, и почему это происходит?

Я прочитал список файлов из TXT-файла (каждый файл и путь в одной строке), и я использую ubuntu 14.04. Благодаря!

ответ

2

Оболочка правильно передает имя файла rsync, но rsync интерпретирует пробелы как разделяющие несколько путей на одном сервере. Поэтому в дополнение к двойному цитированию расширения переменной, чтобы убедиться, что rsync видит строку в виде единственного аргумента, вы также должны указывать пробелы внутри имени файла также.

Если имена файлов не апострофа в них, вы можете сделать это с одинарные кавычки внутри двойных кавычек:

rsync -aP "[email protected]$server:'$LINE'" "$local_folder" 

Если ваши имена файлов могут иметь апострофа в них, то вам необходимо процитировать те (независимо от того, имеют ли имена файлов пробелы). Вы можете использовать встроенную подстановку параметров bash для этого (пока вы находитесь в bash 4, более старые версии, такие как /bin/bash, которые отправляются на OS X, имеют проблемы с обратными косыми чертами и апострофами в таких выражениях). Вот как это выглядит:

rsync -aP "[email protected]$server:'${LINE//\'/\'\\\'\'}'" "$local_folder" 

Уродливый, я знаю, но эффективен. Объяснение следует после других опций.

Если вы используете старый Баш или другую оболочку, вы можете использовать вместо sed:

rsync -aP "[email protected]$server:'$(sed "s/'/'\\\\''/g" <<<"$LINE")'" "$local_folder" 

... или если ваша оболочка также не поддерживает <<< здесь-строки:

rsync -aP "[email protected]$server:'$(echo "$LINE" | sed "s/'/'\\\\''/g")'" "$local_folder" 

Пояснение: мы хотим заменить все апострофы на .. то, что становится буквальным апострофом в середине строки с одной кавычкой. Так как нет никакого способа избежать чего-либо внутри одинарных кавычек, мы должны сначала закрыть кавычки, затем добавить буквальный апостроф, а затем снова открыть кавычки для остальной строки. Фактически это означает, что мы хотим заменить все вхождения апострофа (') на последовательность (апостроф, обратная косая черта, апостроф, апостроф): '\''. Мы можем это сделать либо с расширением параметра bash, либо с sed.

В ударе, ${varname/old/new} расширяется к значению переменной $varname с первым вхождением строки old заменены строками new. Удвоение первой косой черты (${varname//old/new}) заменяет все вхождения вместо первого. Это то, чего мы хотим здесь. Но так как оба апострофа и обратная косая черта являются особыми для оболочки, мы должны поставить (более) обратную косую черту перед каждым из этих символов в обоих выражениях. Это превращает наше значение old в \', а наш new один в \'\\\'\'.

Версия sed немного проще, поскольку апострофы не являются особыми. Обратные косые черты все еще есть, поэтому мы должны положить \\ в строку, чтобы получить \ назад. Поскольку нам нужны апострофы в строке, проще использовать строку с двумя кавычками вместо однокасканной, но это значит, что нам нужно снова удвоить все обратные косые черты , чтобы убедиться, что оболочка передает их на sed unmolested. Вот почему команда оболочки имеет \\\\: она передается sed как \\, который выводится как \.

+0

Я наткнулся на файлы с апострофами. В любом случае, я могу обойти это? –

+0

@ Vini.g.fer: см. Мое редактирование. –

3

rsync делит пространство по умолчанию. Вы можете отключить его с помощью флага (или --protect-args) или вы можете избежать пробелов в имени файла

+0

Этот вопрос отмечен для Ubuntu, так что это хороший ответ, но другие должны заметить, что версия 'rsync', которая поставляется с OS X, к сожалению, не поддерживает опцию' --protect-args'. –

+0

Это работало на моем Ubuntu 14.04, но, как заметил Марк Рид, не будет работать на OS X. Но спасибо! Я подорожаю за вашу помощь;) –

Смежные вопросы