On 29May2019 22:37, Fc Zwtyds <fczwt...@gmail.com> wrote:
在 2019-05-27 11:19, Cameron Simpson 写道:
The output of "ssh-agent -s" is Bourne shell variable assignment syntax. You need to parse that [...]

I want to rewrite the shell script to python script so I have had changed the "ssh-agent -s" to 'ssh-agent cmd' in python script for the consistence on windows.

I'm not sure you need to do that. In fact, I'm fairly sure you don't.

When you run "ssh-agent cmd" the agent starts as before, but instead of reporting its communication socket and process id it dispatches command and runs for the duration of that command, then exits.

Before ssh-agent dispatches "cmd" (here I mean a generic command, though that command might well be "cmd.exe"), it first puts the necessary environment variables into the command's environment.

There are two such values: the communication socket and the ssh-agent process id. The socket is so that the other ssh commands can talk to it, and the process id is so that it can be terminated when no longer wanted. For the "ssh-agent cmd" form there's no need to use the process id, since ssh-agent itself will exit after "cmd" finishes.

Let's look at what "ssh-agent -s" produces. This is on a UNIX system (my Mac):

 [~]fleet*> ssh-agent -s
 SSH_AUTH_SOCK=/Users/cameron/tmp/ssh-vuvXU6vrCAxz/agent.50746; export 
SSH_AUTH_SOCK;
 SSH_AGENT_PID=50754; export SSH_AGENT_PID;
 echo Agent pid 50754;

You can see the socket path and the process id there. The paths will be a bit different on Windows.

So if I do a process listing (this is a UNIX "ps" command, you will need to do the equivalent Windows thing) and search for that process id we see:

 [~]fleet*1> ps ax | grep 62928
 62928   ??  Ss     0:00.00 ssh-agent -s
 66204 s027  S+     0:00.00 grep 62928

So there's the ssh-agent process and also the "grep" command itself because the process id is present on its command line.

Note that at this point my shell (cmd.exe equivalent in Windows) does not know about the new ssh-agent. This is because I haven't put those environment values into the shell environment: the output above is just written to the display. So when I go:

 [~]fleet*> ssh-add -l

it shows me 4 ssh keys. This is because I already have an agent which has some keys loaded. If my shell were talking to the new agent the list would be empty. Let's do that.

 [~]fleet*> SSH_AUTH_SOCK=/Users/cameron/tmp/ssh-vuvXU6vrCAxz/agent.50746
 [~]fleet*> SSH_AGENT_PID=50754
 [~]fleet*> export SSH_AUTH_SOCK SSH_AGENT_PID
 [~]fleet*> ssh-add -l
 The agent has no identities.

So now this shell is using the new agent. You see there's no magic here: other commands do not know about the agent until we tell then about it using these environment variables.

What I was suggesting is that you perform this process from Python, which I believe was your original plan.

So let's adapt the the comand you presented below, and also dig into why what you've tried hasn't worked. So, your subprocess call:

 import subprocess
 p = subprocess.Popen('cmd', stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
stderr = subprocess.PIPE, shell = True, universal_newlines = True)
 outinfo, errinfo = p.communicate('ssh-agent cmd\n')
 print('outinfo is \n %s' % stdoutinfo)
 print('errinfo is \n %s' % stderrinfo)

This does the follow things:

1: Start a subprocess running "cmd". You get back a Popen object "p" for use with that process.

2: Run p.communicate("ssh-agent cmd\n"). This sends that to the input of the "cmd" subprocess and collects the output.

An important thing to know here is the .communicate is a complete interaction with the subprocess. The subprocess' _entire_ input is the string you've supplied, and the entire output of the subprocess until it completes is collected.

So, what's going on is this:

1: start "cmd"
2: send "ssh-agent cmd\n" to it.
3: collect output and wait for it to exit.

From "cmd"'s point of view:

1: Receive "ssh-agent cmd\n", causing it to run the "ssh-=agent cmd" command and then wait for it to exit.

2: ssh-agent starts and then runs another "cmd" and waits for _that_ to exit.

3: The second "cmd" processes its input, then exits. Note that becuase the input to the entire subprocess was "ssh-agent cmd\n", and the first "cmd" consumed that, there is no no more input data. So the second cmd exits immediately because there is no input.

4: The ssh-agent exist because the second "cmd" exited.

5: The first "cmd" sees the "ssh-agent cmd" command exit and looks for
another command to run from its input.

6: As before, there are no more input data. So the first "cmd" also exits.

And at that point .communicate sees the end of the output and returns it.

Because the subprocess is complete, your commented out "p.write" call fails. You can't do things that way with .communicate.

There are complicated (and error prone) ways to do more incremental input, not using .communicate. Let us not go there for now.

Instead, let's consider using communicate with your original plan, which was to reproduce a script when went:

 ssh-agent -s
 ssh-add id_rsa
 ... more ssh based commands here ...

You were using os.system(), which just dispatches a command and doesn't do anything about its input or output. We want to use .communicate because it lets us collect the agent information.

The important thing to note here is that the _only_ extra information the commands after "ssh-agent" require is the environment variables.

So you can go:

1: Dispatch "ssh-agent -s" with subprocess and use .communicate to collect the output.

2: Parse the environment values out of the output and install them in Python's os.environ dictionary.

3: Run your other ssh commands. because os.environ has the necessary values in it, they will communicate with the agent, which is still sitting around.

4: When finished, kill the agent to tidy up.

So start with this:

 p = subprocess.Popen('ssh-agent -s', stdin = subprocess.PIPE, stdout = 
subprocess.PIPE, stderr = subprocess.PIPE, shell = True, universal_newlines = 
True)
 outinfo, errinfo = p.communicate('ssh-agent cmd\n')

You should get the output from ssh-agent in "outinfo". Remember, it is a single string looking like this:

 SSH_AUTH_SOCK=/Users/cameron/tmp/ssh-vuvXU6vrCAxz/agent.50746; export 
SSH_AUTH_SOCK;
 SSH_AGENT_PID=50754; export SSH_AGENT_PID;
 echo Agent pid 50754;

Obviously the specific paths and numbers will vary.

So you want to parse that. Untested code:

 import os

 # break the output data into lines
 lines = outinfo.split('\n')
 for line in lines:
   # trim leading and trailing whitespace
   line = line.strip()
   # ignore blank/empty lines
   if not line:
     continue
   # break off the part before the semicolon
   left, right = line.split(';', 1)
   if '=' in left:
     # get variable and value, put into os.environ
     varname, varvalue = left.split('=', 1)
     print("got", varname, "=', "varvalue)
     os.environ[varname]=varvalue

The you could just use os.system() to run the other commands, because the environment now has the necessary environment settings.

See how you go.

Cheers,
Cameron Simpson <c...@cskk.id.au> (formerly c...@zip.com.au)
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to