TCL, ожидайте: несколько файлов с SCP

Я смог передавать файлы с помощью scp и ожидать, теперь я попытался загрузить сразу несколько файлов:

#!/usr/bin/expect -f
# Escapes spaces in a text
proc esc text {
    return [regsub -all {\ } $text {\\&}]
}

# Uploads several files to a specified server
proc my_scp_multi {ACCOUNT SERVER PW files newfolder} {
    set timeout 30
    send_user -- "\n"
    spawn scp $files $ACCOUNT@$SERVER:[esc $newfolder]
    match_max 100000
    # Look for password prompt
    expect {
    -re ".*Connection closed.*" {
        sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nThis is most likely induced by too many wrong password-attempts and will last quite a time!"
    }
    -re ".*Permission denied.*" {
        sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nYou entered most likely a wrong password!"
    }
    -re ".*Are.*.*yes.*no.*" {
        send "yes\n"
        exp_continue
        #look for the password prompt
    }
    -re ".*sword.*" {
        # Send password aka $PW
        send -- "$PW\r"
        # send blank line (\r) to make sure we get back to gui
        send -- "\r\n"
        exp_continue
    }

    send_user -- "Upload successful!\n"
    }

    set timeout -1
}

Когда я хочу загрузить несколько файлов, используйте команду sh: scp $a $b $c user@server:$folder, поэтому я вызвал my_scp_multi "ACCOUNT" "SERVER" "PW" "~/testfileA ~/testfileB ~/testfileC" "~/test/". Что также дает этот вывод:

spawn scp ~/testfileA ~/testfileB ~/testfileC user@server:~/test/
user@server's password: 
~/testfileA ~/testfileB ~/testfileC: No such file or directory

Кажется, что "~ / testfileA ~ / testfileB ~ / testfileC" рассматривается как один файл. Но когда я копирую и вставляю scp ~/testfileA ~/testfileB ~/testfileC user@server:~/test/ в консоль, все работает нормально!

Что я делаю неправильно? Я пробовал "\"~/testfileA\" \"~/testfileB\" \"~/testfileC\"" и подобные вещи, но вообще ничего не работало.

Есть идеи или предложения?


РЕДАКТИРОВАТЬ

P.S .: Передаю файлы небольшого размера. Установление соединения - самая большая часть перевода. Это причина, по которой я хочу, чтобы это было сделано в ОДНОМ scp.

П.П.С .: Я немного поигрался и придумал:

my_scp_multi3 "user" "server" "pw" "~/a\ b/testfileA, ~/a\\ b/testfileB, ~/a\\\ b/testfileC" "~/test"

с вашим первым решением, но {*}[split $files ","] и

my_scp_multi2 "user" "server" "pw" "~/a b/testfileA" "~/a\ b/testfileB" "~/a\\ b/testfileC" "~/test"

со вторым решением. Это печатает:

~/a b/testfileA: No such file or directory
~/a\ b/testfileB: No such file or directory
~/a\ b/testfileC: No such file or directory

и

~/a b/testfileA: No such file or directory
~/a b/testfileB: No such file or directory
~/a\ b/testfileC: No such file or directory

(Кстати: я, конечно, переместил файлы :))


Благодаря всем ответам, вот мое решение:

использование \ n \ 0 (нулевой байт) в качестве разделителя, потому что это единственный символ, кроме / и \, который не может использоваться в именах файлов.

#!/usr/bin/expect -f

    # Escapes spaces in a text
    proc esc text {
        return [regsub -all {\ } $text {\\&}]
    }

# Returns the absolute Filepath
proc makeAbsolute {pathname} {
    file join [pwd] $pathname
}

proc addUploadFile {files f} {
    if {$files != ""} {
        set files "$files\0"
    }
    return "$files[makeAbsolute $f]"
}

#Counts all files from an upload-list
proc countUploadFiles {s} {
        set rc [llength [split $s "\0"]] 
        incr rc -1
        return $rc
 }

# Uploads several files from a list (created by addUploadFile) to a specified server
proc my_scp_multi {ACCOUNT SERVER PW files newfolder} {
    foreground blue
    set nFiles [countUploadFiles $files]
    set timeout [expr $nFiles * 60]
        send_user -- "\n"
        spawn scp -r {*}[split $files "\0"] $ACCOUNT@$SERVER:[esc $newfolder]
        match_max 100000
        # Look for password prompt
        expect {
        -re ".*Connection closed.*" {
            sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nThis is most likely induced by too many wrong password-attempts and will last quite a time!"
        }
        -re ".*Permission denied.*" {
            sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nYou entered most likely a wrong password!"
        }
        -re ".*Are.*.*yes.*no.*" {
            send "yes\n"
            exp_continue
            #look for the password prompt
        }
        -re ".*sword.*" {
            # Send password aka $PW
            send -- "$PW\r"
            # send blank line (\r) to make sure we get back to gui
            send -- "\r\n"
            exp_continue
        }

        send_user -- "Upload successful!\n"
        }

        set timeout -1
    }



set fls [addUploadFile "" "a b/testfileA"]
set fls [addUploadFile $fls "a b/testfileB"]
set fls [addUploadFile $fls "a b/testfileC"]

my_scp_multi "user" "server" "pw" $fls "~/test"

person 3244611user    schedule 28.01.2014    source источник
comment
технически разделитель каталогов и нулевой байт \0 - единственные запрещенные символы в имени файла. Новая строка - это допустимый символ имени файла. Попробуйте в bash: touch $'a\nfile\nname'. Именно этот факт вызывает массу головных болей у разработчиков сценариев оболочки и разработчиков инструментов: именно поэтому в GNU find есть директива -print0, а в xargs - -0 и т. Д. И т. Д.   -  person glenn jackman    schedule 28.01.2014
comment
Это боль! Итак, вы предлагаете просто использовать \ 0 вместо \ n?   -  person 3244611user    schedule 28.01.2014
comment
почему вы строите строку, а затем разбиваете ее? Просто используйте в первую очередь список: foreach f {"a b/A" "a b/B" "a b/C"} {lappend fls [makeAbsolute $f]}; ... spawn ... {*}$files ...   -  person glenn jackman    schedule 28.01.2014
comment
Совершенно верно. Хммм сейчас работает и мне лень его менять.   -  person 3244611user    schedule 29.01.2014
comment
хм, теперь ты знаешь, на что похожа боль, ты с ней согласен?   -  person glenn jackman    schedule 29.01.2014
comment
Я должен буду быть в порядке с этим: D Я думаю, есть кое-что, чего я действительно не понимаю. Например, когда я вхожу на сервер через ssh, а затем send lpr-Command, например lpr a b.pdf, lpr a\ b.pdf, lpr a\\ b.pdf или lpr a\\\ b.pdf, ничего работать не будет. Опять же, когда я вставлю код в консоль, он заработает? Разве tcl не отправляет команду в виде текста?   -  person 3244611user    schedule 29.01.2014
comment
Хе-хе, приятно поработать с lpr 'a b.pdf'. Но я до сих пор не понимаю, почему команда не работает, когда работает скопированная.   -  person 3244611user    schedule 29.01.2014
comment
потому что Tcl - это не ш. Используйте двойные кавычки или {фигурные скобки} для группировки вместо того, чтобы полагаться на экранирующие пробелы.   -  person glenn jackman    schedule 29.01.2014


Ответы (3)


Вы не хотите отправлять имена файлов в виде одной строки. Либо сделайте это:

spawn scp {*}[split $files] $ACCOUNT@$SERVER:[esc $newfolder]

И продолжайте цитировать имена файлов:

my_scp_multi "ACCOUNT" "SERVER" "PW" "~/testfileA ~/testfileB ~/testfileC" "~/test/"

или сделайте следующее:

proc my_scp_multi {ACCOUNT SERVER PW args} {
    set timeout 30
    send_user -- "\n"
    set files [lrange $args 0 end-1]
    set newfolder [lindex $args end]
    spawn scp {*}$files $ACCOUNT@$SERVER:[esc $newfolder]

И затем не цитируйте имена файлов

my_scp_multi "ACCOUNT" "SERVER" "PW" ~/testfileA ~/testfileB ~/testfileC "~/test/"

Знак ({*}) разбивает список на отдельные элементы, поэтому команда spawn видит несколько слов, а не одно слово. См. http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm

person glenn jackman    schedule 28.01.2014
comment
Спасибо! Отличный ответ! Это очень хорошо работает. Итак, scp здесь - это tcl-команда, а не обычная оболочка scp? - person 3244611user; 28.01.2014
comment
Поскольку scp искал единственный файл с именем ~/testfileA ~/testfileB ~/testfileC (с пробелами) и не смог его найти. Вам нужно предоставить scp с отдельными именами файлов, и вы хотите, чтобы это делал специальный синтаксис Tcl. - person glenn jackman; 28.01.2014
comment
Хорошо, все идет отлично! Остается только одна проблема: пробелы в именах файлов; Либо одно из ваших решений не работает. Подробнее читайте в моем посте. - person 3244611user; 28.01.2014
comment
Это потому, что вы вставляете буквальную обратную косую черту в имя файла. Достаточно просто цитирования: my_scp_multi2 "user" "server" "pw" "~/a b/testfileA" "~/a b/testfileB" "~/a b/testfileC" "~/test" - person glenn jackman; 28.01.2014
comment
Даже "~/a b/testfileA" "~/a\ b/testfileB" "~/a\\ b/testfileC" выдает одинаковую ошибку для всех трех файлов ... - person 3244611user; 28.01.2014
comment
возможно, ~ не расширяется. Сделайте cd перед появлением и используйте "a b/testfileA" "a b/testfileB" "a b/testfileC" - person glenn jackman; 28.01.2014
comment
Да, действительно было. Забавно, как цель (и для отдельных файлов) он работает с ~ ... Еще раз спасибо! - person 3244611user; 28.01.2014

Вы можете создать оболочку, а затем вместо этого запустить команду scp:

spawn bash
send "scp $files $ACCOUNT@$SERVER:[esc $newfolder]\r"

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

-re "100%" {
    if { $index < $count } {
        set index [expr $index + 1]
        exp_continue
    }
}

Где index - это номер передаваемого файла и подсчитайте количество файлов.

person Timmah    schedule 28.01.2014
comment
Хорошая идея, но звучит довольно сложно по сравнению с ответом Гленна. - person 3244611user; 28.01.2014

Вы должны использовать аутентификацию с открытым ключом SSH вместо того, чтобы вводить пароль с expect. При правильной настройке scp будет работать без ввода паролей человеком, сохраняя при этом безопасность системы. Вы избавитесь от всех неприятностей с expect.

Если по какой-то причине вы не можете использовать pubkey, вы можете найти sftp полезным, потому что он принимает файл пакетных команд как -b batchfile. См. man 1 sftp Не очень хорошее решение, когда expect может фактически разделить аргументы

person nodakai    schedule 28.01.2014
comment
Спасибо! На самом деле есть причина, по которой я не могу (и не хочу) использовать аутентификацию с открытым ключом. sftp мог бы быть решением, но я думаю, что ответ Гленна проще. - person 3244611user; 28.01.2014