Suppose I want to wrap ssh-keygen
into an non-interactive script named my-keygen
, so that I can use it like
echo "myinfo1\nmyinfo2\nmysecret3" | my-keygen
This brings me to use expect
:
spawn ssh-keygen
expect "Enter file in which to save the key..."
set line [gets stdin]
send "$line\n"
...repeat for more questions...
interact
And I find it works when I answer questions from tty, but it not works if the stdin is from pipe.
I searched a lot and found something useful:
- How to send standard input through a pipe
- Can expect scripts read data from stdin?
- How can I pipe initial input into process which will then be interactive?
Which let me know that there is an EOF
in the piped content which will cause the interactive program exit without finish it's job.
A minimal example to reproduce what happend is:
create a file named
test
with following content#!/usr/bin/env expect spawn bash interact
run
chmod x ./test
to make it executablerun
echo 'echo hello' | ./test
and confirm thathello
is not printed as expectedrun
echo 'echo hello' | bash
and confirm thathello
is printed as expected this way
So is there any solution to make ./test
works as expect?
CodePudding user response:
The way you're echoing your string, the newlines are being sent as literal characters and not being interpreted as newlines so there is only one line of stdin being passed to expect that gets completely consumed the first time you read stdin. There are a couple ways to fix this:
- Use
echo -e
echo -e "myinfo1\nmyinfo2\nmysecret3" | my-keygen
echo "myinfo1"$'\n'"myinfo2"$'\n'"mysecret3" | my-keygen
- Using actual newlines
echo "myinfo1
myinfo2
mysecret3" | my-keygen
- A here-document
my-keygen <<EOF
myinfo1
myinfo2
mysecret3
EOF
CodePudding user response:
(Not a direct answer. Just show the idea how to do it.)
Expect's interact
does not read from the pipe. You need to read data from the pipe by yourself and then forward the data to Expect.
The following is a quick example using my sexpect. You can easily write the Expect script accordingly.
The script foo.sh
:
export SEXPECT_SOCKFILE=/tmp/sexpect-$$.sock
ps1re='bash-[0-9.] [$#] $'
sexpect spawn bash --norc
sexpect expect -re "$ps1re"
while read -r cmd; do
sexpect send -cr "$cmd"
sexpect expect -re "$ps1re"
done
sexpect send -cr "exit"
sexpect wait
Let's try it:
$ printf '%s\n' tty 'echo hello world' | bash foo.sh
bash-5.2$ tty
/dev/ttys013
bash-5.2$ echo hello world
hello world
bash-5.2$ exit
exit
$
CodePudding user response:
The Expect version of @sexpect's answer:
$ cat foo.exp
proc expect_prompt {} {
upvar spawn_id spawn_id
expect -re {bash-[.0-9] [#$] $}
}
spawn -noecho bash --norc
expect_prompt
while { [gets stdin cmd] >= 0 } {
send "$cmd\r"
expect_prompt
}
send "exit\r"
expect eof
$ printf '%s\n' tty 'echo hello world' | expect foo.exp
bash-5.1$ tty
/dev/pts/13
bash-5.1$ echo hello world
hello world
bash-5.1$ exit
exit