【问题标题】:TCL, expect: Multiple files w/ SCPTCL,期望:带 SCP 的多个文件
【发布时间】:2014-02-19 20:16:51
【问题描述】:

我可以使用 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.:我正在传输相当小的文件。建立联系是转移的最大部分。这就是我希望它在 ONE 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 (nullbyte) 作为分隔符,因为它是除了 / 和 \ 之外的唯一符号,不能在文件名中使用。

#!/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"

【问题讨论】:

  • 从技术上讲,目录分隔符和空字节\0 是文件名中唯一不允许使用的字符。换行符是一个有效的文件名字符。在 bash 中尝试:touch $'a\nfile\nname'。这个事实让 shell 脚本编写者和工具开发者头疼不已:这就是为什么 GNU find 有 -print0 指令,xargs 有 -0 等等
  • 这很痛苦!所以你建议只使用 \0 而不是 \n ?
  • 你为什么要构造一个字符串然后拆分它?首先使用一个列表:foreach f {"a b/A" "a b/B" "a b/C"} {lappend fls [makeAbsolute $f]}; ... spawn ... {*}$files ...
  • 完全正确。嗯,现在可以了,我懒得改了。
  • 因为 Tcl 不是 sh。使用“双引号”或 {braces} 进行分组,而不是依赖转义空格。

标签: linux tcl expect scp


【解决方案1】:

您不想将文件名作为单个字符串发送。要么这样做:

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/"

splat ({*}) 将列表拆分为单独的元素,因此 spawn 命令会看到多个单词,而不是单个单词。见http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm

【讨论】:

  • 谢谢!很好的答案!这很好用。所以这里的 scp 是一个 tcl 命令,而不是“正常”的 shell scp?
  • 因为 scp 正在寻找一个名为 ~/testfileA ~/testfileB ~/testfileC(带有空格)的文件,但它找不到。您需要为 scp 提供单独的文件名,并且您希望这种特殊的 Tcl 语法能够做到这一点。
  • 好的,运行得很好!只剩下一个问题:文件名中的空格;您的任何一种解决方案都不起作用。在我的帖子中查看更多信息。
  • 那是因为你在文件名中插入了一个反斜杠。只需引用即可:my_scp_multi2 "user" "server" "pw" "~/a b/testfileA" "~/a b/testfileB" "~/a b/testfileC" "~/test"
  • 可能是~ 没有被展开。在生成之前执行cd,并使用"a b/testfileA" "a b/testfileB" "a b/testfileC"
【解决方案2】:

您应该使用 SSH 公钥身份验证,而不是使用 expect 输入密码。正确设置后,scp 无需任何人工输入密码即可工作,同时保持系统非常安全。使用expect,您将摆脱所有烦恼。

如果由于某些原因无法使用 pubkey,您可能会发现 sftp 很有用,因为它接受批处理命令文件为 -b batchfile。见man 1 sftpexpect实际上可以拆分参数时,这不是一个很好的解决方案

【讨论】:

  • 谢谢!实际上,我不能(也不想)使用公钥身份验证是有原因的。 sftp 可能是一个解决方案,但我认为 glenn 的答案更容易。
【解决方案3】:

您可以生成一个 shell,然后运行 ​​scp 命令:

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

这允许全局扩展,但会增加额外的内务处理,因为您需要在 scp 进程完成时进行陷阱,因为您仍然有一个 shell 正在运行。 您可以将以下内容添加到您的期望块中:

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

其中 index 是正在传输的文件的数量并计算文件的 nr。

【讨论】:

  • 好主意,但与格伦的回答相比,听起来相当复杂。
猜你喜欢
  • 2014-11-05
  • 2012-04-29
  • 2014-01-16
  • 2019-03-09
  • 2012-11-10
  • 1970-01-01
  • 2015-03-20
  • 2011-11-21
  • 2018-09-04
相关资源
最近更新 更多