I have an expect script to download the lastest database backup from a remote server. I am new to shell/expect scripting and I am struggeling with storing a clean filename from the output buffer in a variable. Here is what I got so far:

#!/usr/bin/expect -f
set dbname [lindex $argv 0]
spawn ssh "sshuser@remote_ip"
expect "password: "
send "MySSHPass\r"
expect "$ "
send "cd /var/backup/dumps\r"
expect "$ "
send "ls -tl | grep --color=never -o -m1 \"\\<$dbname.*\\>\"\r"

expect "\r" # flushing the previous output from the buffer done right?
expect ".gz"
# the resulting string seems to have a leading newline or return char
set filename $expect_out(buffer)
expect "$ "
send "exit\r"

spawn sftp "sshuser@remote_ip"
expect "password:"
send "MySSHPass\r"
expect "sftp>"
send "lcd database_dumps\r"
expect "sftp>"
send "get /var/backup/dumps/$filename\r"
expect "sftp>"
send "exit\r"

The extracted filename seems to have a leading newline or return char, so the sftp get path is not concatenated right... Any advice how to do this right?

  • 2
    Can you, please, edit your post with the output you get ? I have a doubt regarding the \r you use. Usually, in Unix/Linux world, \n is used...
    – binarym
    Jan 15, 2020 at 12:49
  • @binarym Yes, you were right, I replaced all \r with \n and now it works!
    – Benni
    Jan 15, 2020 at 12:58
  • @binarym Running expect -d as suggested by glenn jackman showed that lines in fact are terminated by \r\n
    – Benni
    Jan 17, 2020 at 7:30

1 Answer 1


First point, you don't need to grep the output of ls: this should suffice

send "ls -1t $dbname.*\r"

And to grab just the newest one:

send "ls -1t $dbname.* | head -1\r"

Now to the point of your question: yes it can be a hassle extracting the command output from the expect_out buffer. Do this:

expect -re {(.*)\r\n$ $}
set cmd_output $expect_out(1,string)

cmd_output will contain the shell command you sent, plus the output, all lines separated by \r\n. You can inspect it with

exec od -c <<$expect_out(buffer)

You need to remove the first \r\n separated line. Here's one way to do that:

if {![regexp {^.+?\r\n(.*)$} $cmd_output -> filename]} {
    error "unexpected output: does not contain \\r\\n"
# now, go get $filename
  • Thanks for your useful hints, but the line set cmd_output $expect_out(1,string) outputs can't read "expect_out(1,string)": no such element in array... for me, even though the ls call has found a file. So I suppose there is something wrong with the curly braces part. If I change it to maybe expect -re "(.*)\n", $expect_out(1,string) contains the sent command
    – Benni
    Jan 16, 2020 at 12:48
  • 1
    My regex is looking for everything before your prompt. I'd suggest running with expect -d and it will show you clues about why the pattern didn't match Jan 16, 2020 at 13:05
  • That did the trick for me, with #!/usr/bin/expect -d I was able to adjust the regular expression to {\r\n(.*)\r\n} and now it works. BTW, what mean the curly braces in this context? Are they just a different way to delimit a string?
    – Benni
    Jan 17, 2020 at 7:24
  • Yes. Refer to tcl.tk/man/tcl8.6/TclCmd/Tcl.htm rule number 6 Jan 17, 2020 at 11:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .