I am trying to port some simple scripts that I have in tcl to python.
Using tcl/expect, we can see every executed command on the standard output. For example,
spawn ssh admin#$IP
send "ls\r"
would yield by default an output like this:
ssh admin#10.10.10.10
ls
....
In python, only way I saw was to decode the child.before or after outputs.
Is there a way python can output everything it runs to the console or a file ?
This is what I am doing now:
#!/usr/bin/env python3
import pexpect
shellprompt = "] # "
child = pexpect.spawn('ssh admin#XYZ')
child.sendline('ls')
child.expect(shellprompt)
ls_out = child.before.decode()
print(ls_out)
This is run on a Linux machine, and doing ssh to a Linux machine
Related
In the past week I install a Terraria 1.3.5.3 server into an Ubuntu v18.04 OS, for playing online with friends. This server should be powered on 24/7, without any GUI, only been accessed by SSH on internal LAN.
My friends ask me if there is a way for them to control the server, e.g. send a message, via internal in-game chat, so I thought use a special character ($) in front of the desired command ('$say something' or '$save', for instance) and a python program, that read the terminal output via pipe, interpreter the command and send it back with a bash command.
I follow these instructions to install the server:
https://www.linode.com/docs/game-servers/host-a-terraria-server-on-your-linode
And config my router to forward a dedicated port to the terraria server.
All is working fine, but I really struggle to make python send a command via "terrariad" bash script, described in the link above.
Here is a code used to send a command, in python:
import subprocess
subprocess.Popen("terrariad save", shell=True)
This works fine, but if I try to input a string with space:
import subprocess
subprocess.Popen("terrariad \"say something\"", shell=True)
it stop the command in the space char, output this on the terminal:
: say
Instead of the desired:
: say something
<Server>something
What could I do to solve this problem?
I tried so much things but I get the same result.
P.S. If I send the command manually in the ssh putty terminal, it works!
Edit 1:
I abandoned the python solution, by now I'll try it with bash instead, seem to be more logic to do this way.
Edit 2:
I found the "terrariad" script expect just one argument, but the Popen is splitting my argument into two no matter the method I use, as my input string has one space char in the middle. Like this:
Expected:
terrariad "say\ something"
$1 = "say something"
But I get this of python Popen:
subprocess.Popen("terrariad \"say something\"", shell=True)
$1 = "say
$2 = something"
No matter i try to list it:
subprocess.Popen(["terrariad", "say something"])
$1 = "say
$2 = something"
Or use \ quote before the space char, It always split variables if it reach a space char.
Edit 3:
Looking in the bash script I could understand what is going on when I send a command... Basically it use the command "stuff", from the screen program, to send characters to the terraria screen session:
screen -S terraria -X stuff $send
$send is a printf command:
send="`printf \"$*\r\"`"
And it seems to me that if I run the bash file from Python, it has a different result than running from the command line. How this is possible? Is this a bug or bad implementation of the function?
Thanks!
I finally come with a solution to this, using pipes instead of the Popen solution.
It seems to me that Popen isn't the best solution to run bash scripts, as described in How to do multiple arguments with Python Popen?, the link that SiHa send in the comments (Thanks!):
"However, using Python as a wrapper for many system commands is not really a good idea. At the very least, you should be breaking up your commands into separate Popens, so that non-zero exits can be handled adequately. In reality, this script seems like it'd be much better suited as a shell script.".
So I came with the solution, using a fifo file:
First, create a fifo to be use as a pipe, in the desired directory (for instance, /samba/terraria/config):
mkfifo cmdOutput
*/samba/terraria - this is the directory I create in order to easily edit the scripts, save and load maps to the server using another computer, that are shared with samba (https://linuxize.com/post/how-to-install-and-configure-samba-on-ubuntu-18-04/)
Then I create a python script to read from the screen output and then write to a pipe file (I know, probably there is other ways to this):
import shlex, os
outputFile = os.open("/samba/terraria/config/cmdOutput", os.O_WRONLY )
print("python script has started!")
while 1:
line = input()
print(line)
cmdPosition = line.find("&")
if( cmdPosition != -1 ):
cmd = slice(cmdPosition+1,len(line))
cmdText = line[cmd]
os.write(outputFile, bytes( cmdText + "\r\r", 'utf-8'))
os.write(outputFile, bytes("say Command executed!!!\r\r", 'utf-8'))
Then I edit the terraria.service file to call this script, piped from terrariaServer, and redirect the errors to another file:
ExecStart=/usr/bin/screen -dmS terraria /bin/bash -c "/opt/terraria/TerrariaServer.bin.x86_64 -config /samba/terraria/config/serverconfig.txt < /samba/terraria/config/cmdOutput 2>/samba/terraria/config/errorLog.txt | python3 /samba/terraria/scripts/allowCommands.py"
*/samba/terraria/scripts/allowCommands.py - where my script is.
**/samba/terraria/config/errorLog.txt - save Log of errors in a file.
Now I can send commands, like 'noon' or 'dawn' so I can change the in-game time, save world and backup it with samba server before boss fights, do another stuff if I have some time XD, and have the terminal showing what is going on with the server.
I am attempting to dump a python script's output to netcat using WSL on windows. In one terminal, I have my python program & netcat set up in listener mode. In another terminal, I am connecting to this listener and expecting to see my python output. The below code works and I am able to do this successfully, but when adding time.sleep(1) inbetween function calls I do not receive any Python output. Why is this?
I have tried using the print() function instead of sys.stdout.write(). I've tried different nc configurations.
Python producer script:
import time
from sys import stdout
def generate_stream_data():
stdout.write('some text\n')
if __name__ == '__main__':
iterations = 10000
while iterations > 0:
generate_stream_data()
time.sleep(1) # this causes nothing to get piped to nc
Piping the python output in WSL:
python above_code.py | nc -lk localhost 9999
In another WSL terminal:
nc -v localhost 9999
Commenting out time.sleep(1) lets me see my output in my second terminal. I have tested the standalone Python program, and it successfully prints 'some_ text' to console with the 1sec delay. Why does adding this cause me to not see anything in netcat?
I got two functions: both for starting Server on particular port. Something like:
from Multiprocessing import Process
import sys
def start_server1(port1):
os.system("server1 --port %s" % port1)
def start_server2(port2):
os.system("server2 --port %s" % port2)
port1 = arg.sys[1]
port2 = arg.sys[2]
p1 = Process(target=start_server1, args=(port1,))
p2 = Process(target=start_server2, args=(port2,))
This allow me to start both servers from terminal within my script like
>>>python servers.py 8000 8001
But a lot of traces are expected to be displayed for both servers and I want to see them separately. So the question is: how to make so that script will be executed from one terminal shell, process p1 will be started in new shell as well as second process p2?
Thank you for advises and suggestions
You can redirect the output of each process in a file.
os.system("server1 --port %s > server1.log" % port1)
Then, run tail command on each logs in a separate shell to monitor them.
tail -f server1.log
However, if you are not interested to store the output of those process in a file and want a separate shell solution, you can use "xterm -e" option.
The -e option of xterm allows to run a shell command in a separate xterm window.
os.system('xterm -e "server1 --port %s"'% port)
I need to run a command via rpyc and get the result of this command.
But whenever I run the command, it is printed on the remote server and can not bring the result.
What I am doing is the following:
import os, sys
import rpyc
conn = rpyc.classic.connect('server_remote')
a = conn.modules.os.system( "ls -lh")
print a
The output of my command is 0 for any command I run.
python get_output.py
0
Use os.popen or subprocess.checked_output instead of system. System just returns the exit code, while the output is printed to your stdout (i.e. you don't get hold of it programmatically)
I need to launch a server on the remote machine and retrieve the port number that the server process is lsitening on. When invoked, the server will listen on a random port and output the port number on stderr.
I want to automate the process of logging on to the remote machine, launching the process, and retrieving the port number. I wrote a Python script called "invokejob.py" that lives on the remote machine to act as a wrapper that invokes the job and then returns the port number, it looks like this:
import re, subprocess
executable = ... # Name of executable
regex = ... # Regex to extract the port number from the output
p = subprocess.Popen(executable,
bufsize=1, # line buffered
stderr=subprocess.PIPE
)
s = p.stderr.readline()
port = re.match(regex).groups()[0]
print port
If I log in interactively, this script works:
$ ssh remotehost.example.com
Last login: Thu Aug 28 17:31:18 2008 from localhost
$ ./invokejob.py
63409
$ exit
logout
Connection to remotehost.example.com closed.
(Note: successful logout, it did not hang).
However, if I try to invoke it from the command-line, it just hangs:
$ ssh remotehost.example.com invokejob.py
Does anybody know why it hangs in the second case, and what I can do to avoid this?
Note that I need to retrieve the output of the program, so I can't just use the ssh "-f" flag or redirect standard output.
s = p.stderr.readline()
I suspect it's the above line. When you invoke a command directly through ssh, you don't get your full pty (assuming Linux), and thus no stderr to read from.
When you log in interactively, stdin, stdout, and stderr are set up for you, and so your script works.
what if you do the following:
ssh <remote host> '<your command> ;<your regexp using awk or something>'
For example
ssh <remote host> '<your program>; ps aux | awk \'/root/ {print $2}\''
This will connect to , execute and then print each PSID for any user root or any process with root in its description.
I have used this method for running all kinds of commands on remote machines. The catch is to wrap the command(s) you wish to execute in single quotation marks (') and to separate each command with a semi-colon (;).