简体   繁体   中英

Get one line of user input and then execute it as Bash commands

I have wrote a expect script that helps to execute commands in remote machine. When the execution is completed, I want to get one line of user input and then send it to the remote bash, here is the code snippet:

#! /usr/bin/env expect
...
spawn ssh -l $user $host
...
send_tty -- "Enter your command: " 
set timeout -1

# match only printable characters (prevent from pressing TAB)
expect_tty eof exit -re {([[:print:]]*)\n}
send_tty -- "\n"
set timeout 10

# send the command to remote shell
send "$expect_out(1,string)" 
expect "$GENERAL_PROMPT"

However, if the input is something like: ls /" , my program will be blocked because the remote shell expects to get more characters by prompting the string "> ". Actually, I hope bash won't prompt for more input instead of just printing error message:

$ read COMMAND
ls /"
$ eval "$COMMAND"
bash: unexpected EOF while looking for matching `"'
bash: syntax error: unexpected end of file

Can I achieve this in my script?

#!/usr/bin/expect
set prompt "#|%|>|\\\$ $"; # A generalized prompt to match known prompts.
spawn ssh -l dinesh xxx.xx.xx.xxx
expect {
    "(yes/no)" { send "yes\r";exp_continue}
    "password" 
}
send "mypassword\r"
expect -re $prompt
send_tty -- "Enter your command: " 
set timeout -1

# match only printable characters (prevent from pressing TAB)
expect_tty eof exit -re {([[:print:]]*)\n}
send_tty -- "\n"
set timeout 10
puts "\nUSER INPUT : $expect_out(1,string)"

# send the command to remote shell 
# Using 'here-doc', to handle possible user inputs, instead of quoting it with any other symbol like single quotes or backticks
send "read COMMAND <<END\r"
expect -re $prompt
send "$expect_out(1,string)\r" 
expect -re $prompt
send "END\r"
expect -re $prompt

# Since we want to send the literal dollar sign, I am sending it within braces
send {eval $COMMAND}
# Now sending 'Return' key
send "\r"
expect -re $prompt

Why 'here-doc' used ?

If I have used backticks or single quotes to escape the commands, then if user gave backticks or single quotes in the commands itself, then it may fail. So, to overcome that only, I have added here-doc.

Output :

dinesh@MyPC:~/stackoverflow$ ./zhujs
spawn ssh -l dinesh xxx.xx.xx.xxx
dinesh@xxx.xx.xx.xxx's password: 

[dinesh@lab ~]$ matched_literal_dollar_sign
Enter your command: ls /"


USER INPUT : ls /"
read COMMAND <<END
> ls /"
> END
[dinesh@lab ~]$ eval $COMMAND
-bash: unexpected EOF while looking for matching `"'
-bash: syntax error: unexpected end of file
[dinesh@lab ~]$ dinesh@MyPC:~/stackoverflow$ 

Update :

The main reason for using here-doc is due to the fact that it makes the read to act as non-blocking command. ie We can proceed quickly with next command. Else, we have to wait till the timeout of Expect . (Of course, we could change the timeout value dynamically.)

This is just one way of doing it. You can alter it if you want, with simply having the read command.

I think this would be a good case for interact -- get expect to step aside and let the user interact directly with the spawned program.

spawn ssh -l $user $host
#...

send_user "You are now about to take control: type QQQ to return control to the program\n"

interact {
    QQQ   return
}

send_user "Thanks, I'm back in charge ...\n"

这是一个单行版本

read > export cmd ; eval $cmd ; unset cmd

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM