I want to execute a python script, which switches to another user by automatically writing the user password. Both users have no root rights. After the login I want to execute the OS Commands "whoami" to check if the login was successful. Here's the code:
child = pexpect.spawn('su - otheruser)
child.expect_exact('Password:')
child.sendline('password')
print("logged in...")
child.expect('')
child.sendline('whoami')
print(child.before)
I want to print the output from the command to the console (just for debugging) but the output is like "b272' (a combination of random letters) and not the actual whoami user. How can I fix that?
Later I want to create from the switched user some files and so on. So basically, I want to execute OS Commands in a python script which is logged in an other user.
Pexpect searches are not greedy, so it will stop at the first match. When I tested your code with before, match.groups(), after, and buffer, I didn't get an EOF or TIMEOUT, so it must have matched right at the beginning of the read and returned nothing (I'm surprised you got any results at all).
I recommend always following a sendline with an expect, and the end of a prompt (]$) is a good thing to expect, instead of an empty string.
Here is my take on your code, including creating a file:
NOTE - Tested on Centos 7.9, using Python 2.7.
import pexpect
child = pexpect.spawn("su - orcam")
child.expect_exact("Password:")
child.sendline("**********")
child.expect_exact("]$")
print("Logged in...\n")
child.sendline("whoami")
child.expect_exact("]$")
print(child.before + "\n")
child.sendline("echo -e 'Hello, world.' >> hello.txt")
child.expect_exact("]$")
child.sendline("cat hello.txt")
child.expect_exact("]$")
print(child.before + "\n")
child.sendline("exit")
index = child.expect_exact(["logout", pexpect.EOF, ])
print("Logged out: {0}".format(index))
Output:
Logged in...
whoami
orcam
[orcam#localhost ~
cat hello.txt
Hello, world.
[orcam#localhost ~
Logged out: 0
Related
I want to run the shell command 'su - testuser -c "id"' and get the output. In the console it asks the password after that. My intention is to run it in a python script where it logs into antoher user (neither the source nor the destination user has root rights). The problem is, that the password should be entered non-interactive, so that I can just start the script and see the output without entering the password. So I can start the python script and it automatically runs the command without waiting for the password and gives me the output.
I tried it using the pexpect package:
child = pexpect.spawn('su - testuser -c "id"')
child.expect_exact('Password:')
child.sendline('mypassword')
print(child.before) # Prints the output of the "id" command to the console
The problem is that the code doesn't function. The output is like a random string instead of the id and so on. How can I do that?
Using child.read instead of print(child.before) solves it.
>>> child = pexpect.spawn('su - testuser -c "id"')
>>> child.expect_exact('Password:')
0
>>> child.sendline('1234')
5
>>> print(child.before)
b''
>>> child.read()
b' \r\nuid=1002(testuser) gid=1003(testuser) groups=1003(testuser)\r\n'
You can read more details on here
I am familiar with expect script so I feel a bit odd when I first use pexpect. Take this simple script as an example,
#!/usr/bin/expect
set timeout 10
spawn npm login
expect "Username:"
send "qiulang2000\r"
expect "Password:"
send "xxxxx\r"
expect "Email:"
send "qiulang#gmail.com\r"
expect "Logged in as"
interact
When run it I will get the following output. It feels natural because that is how I run those commands
spawn npm login
Username: qiulang2000
Password:
Email: (this IS public) qiulang#gmail.com
Logged in as qiulang2000 on https://registry.npmjs.com/.
But when I use pexpect, no matter how I add print(child.after)or print(child.before) I just can't get output like expect, e.g. when I run following command,
#! /usr/bin/env python3
import pexpect
child = pexpect.spawn('npm login')
child.timeout = 10
child.expect('Username:')
print(child.after.decode("utf-8"))
child.sendline('qiulang2000')
child.expect('Password:')
child.sendline('xxxx')
child.expect('Email:')
child.sendline('qiulang#gmail.com')
child.expect('Logged in as')
print(child.before.decode("utf-8"))
child.interact()
I got these output, it feels unnatural because that is not what I see when I run those commands.
Username:
(this IS public) qiulang#gmail.com
qiulang2000 on https://registry.npmjs.com/.
So is it it possbile to achieve the expect script output?
--- update ---
With the comment I got from #pynexj I finally make it work, check my answer below.
With the comment I got I finally made it work
#! /usr/bin/env python3
import pexpect
import sys
print('npm login',timeout = 10)
child = pexpect.spawn('npm login')
child.logfile_read = sys.stdout.buffer // use sys.stdout.buffer for python3
child.expect('Username:')
child.sendline('qiulang2000')
child.expect('Password:')
child.sendline('xxxx')
child.expect('Email:')
child.sendline('qiulang#gmail.com')
child.expect('Logged in as')
If I need to call child.interact(), then it is important that I call child.logfile_read = None before it, otherwise sys.stdout will echo everything I type.
The answer here How to see the output in pexpect? said I need to pass an encoding for python3, but I found that if I use encoding='utf-8' it will cause TypeError: a bytes-like object is required, not 'str' If I don't set encoding at all, everything works fine.
So a simple ssh login script looks like this
#!/usr/bin/env python3
import pexpect
import sys
child = pexpect.spawn('ssh qiulang#10.0.0.32')
child.logfile_read = sys.stdout.buffer
child.expect('password:')
child.sendline('xxxx')
#child.expect('Last login') don't do that
child.logfile_read = None # important !!!
child.interact()
One problem remains unresolved, I had added one last expect call to match the ssh login output after sending the password, e.g. child.expect('Last login')
But if I added that, that line would show twice. I have gave up trying, like one comment said "pexpect's behavior is kind of counter intuitive".
Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-141-generic x86_64)
* Documentation: https://help.ubuntu.com/
33 packages can be updated.
0 updates are security updates.
Last login: Fri Sep 11 11:44:19 2020 from 10.0.0.132
: Fri Sep 11 11:44:19 2020 from 10.0.0.132
#! python3
# pw.py - An insecure password locker program.
PASSWORDS = {'email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6',
'blog': 'VmALvQyKAxiVH5G8v01if1MLZF3sdt',
'luggage': '12345'}
import sys, pyperclip
if len(sys.argv) < 2:
print('Usage: py pw.py [account] - copy account password')
sys.exit()
account = sys.argv[1] # first command line arg is the account name
if account in PASSWORDS:
pyperclip.copy(PASSWORDS[account])
print('Password for ' + account + ' copied to clipboard.')
else:
print('There is no account named ' + account)
I really don't know what to do. After running win+r and typing e.g. pw email i get only 'usage:py bla bla bla.. nothing else whatever i wrote in win+r
the bat file is like:
'''call C:\Users\Rostek\anaconda3\Scripts\activate.bat
C:\Users\Rostek\anaconda3\python.exe "C:\Users\Rostek\.spyder-py3\Projekty\pw.py"
#pause'''
I cannot pass the arguments I think. I have read all the internet and found nothing like this.
please help. It's program from the book. I am using anaconda3.
You might want to think about what your problem is supposed to do.
if len(sys.argv) < 2:
print('Usage: py pw.py [account] - copy account password')
sys.exit()
This clearly does what it should.
Your question is actually not about python, but about win+r passing arguments.
Why do you want to run your program with win+r in the first place?
After running win+r and typing e.g. pw email...
What you want is to open the command line/ powershell/ bash instead and simply pass the variables to your programm with python3 programname.py email directly.
If you want to do that even cleaner you should use an argparser.
EDIT:
After the clarification in the comments:
The problem is that if you execute a script with win+r you will get the output- for a fraction of a second- then the cmd is closed...
So unless you specify a location in your script to where the pw is written to, you´ll have it in your console. Which blinks and vanishes immediately. Therefore open a console and execute the program from there.
Or you might want to have a look at this:
Output to clipboard
I am writing a CLI that accepts an email and password for auth.
The email prompt uses raw_input() and the password prompt uses getpass() for obfuscation.
This setup works fine when outputting directly to console, but falters when redirecting the output to a log file.
Sample code:
user_email = raw_input('Email: ')
user_password = getpass('Password: ')
Sample output without redirection:
$ python script_that_does_stuff.py
Email: me#email.com
Password:
Doing stuff...
Sample output with redirection:
$ python script_that_does_stuff.py > stuff.log
Because I know that it's expecting a user input here, I can type the email, hit enter, and then it will show:
$ python script_that_does_stuff.py > stuff.log
me#email.com
Password:
After inputting a password, it continues as usual, however the log shows the following:
$ cat stuff.log
Email:Doing stuff...
Question:
How can I force the raw_input() prompt to show up in console like the getpass() prompt does when redirecting output to a file?
Environment
This script lives in a legacy Python 2.7 codebase, and is run primarily on Mac OS systems, occasionally Linux.
You can override sys.stdout temporarily to write to the terminal. For example,
import contextlib
import sys
#contextlib.contextmanager
def output_to_terminal():
try:
with open("/dev/tty") as f:
sys.stdout = f
yield
finally:
# Ensure sys.stdout is restored in the event of an error
sys.stdout = sys.__stdout__
with output_to_terminal():
x = raw_input("> ")
print(x)
(This was derived independently; you may want to check source for Python 3's redirect_stdout, also found in the contextlib module, and back port it for your use.)
This answer on another question seems to work for me.
In short, create a custom input function:
def email_input(prompt=None):
if prompt:
sys.stderr.write(str(prompt))
return raw_input()
The calling code then becomes:
user_email = email_input('Email: ')
user_password = getpass('Password: ')
This results in both the Email and Password prompts being sent to stderr (printing to console), and not messing with the redirected log output.
According to official documentation getpass([prompt[, stream]]) has the second optional parameter which indicates output stream to print the prompt to (stderr by default).
When you redirect the output (stdout) the prompt is still printed to stderr for getpass but raw_input does not support setting an output stream so its prompt is redirecting to to the target file.
So to solve your issue, you have to print your prompt to stderr for email as well.
So I have this python3 script that does a lot of automated testing for me, it takes roughly 20 minutes to run, and some user interaction is required. It also uses paramiko to ssh to a remote host for a separate test.
Eventually, I would like to hand this script over to the rest of my team however, it has one feature missing: evidence collection!
I need to capture everything that appears on the terminal to a file. I have been experimenting with the Linux command 'script'. However, I cannot find an automated method of starting script, and executing the script.
I have a command in /usr/bin/
script log_name;python3.5 /home/centos/scripts/test.py
When I run my command, it just stalls. Any help would be greatly appreciated!
Thanks :)
Is a redirection of the output to a file what you need ?
python3.5 /home/centos/scripts/test.py > output.log 2>&1
Or if you want to keep the output on the terminal AND save it into a file:
python3.5 /home/centos/scripts/test.py 2>&1 | tee output.log
I needed to do this, and ended up with a solution that combined pexpect and ttyrec.
ttyrec produces output files that can be played back with a few different player applications - I use TermTV and IPBT.
If memory serves, I had to use pexpect to launch ttyrec (as well as my test's other commands) because I was using Jenkins to schedule the execution of my test, and pexpect seemed to be the easiest way to get a working interactive shell in a Jenkins job.
In your situation you might be able to get away with using just ttyrec, and skip the pexpect step - try running ttyrec -e command as mentioned in the ttyrec docs.
Finally, on the topic of interactive shells, there's an alternative to pexpect named "empty" that I've had some success with too - see http://empty.sourceforge.net/. If you're running Ubuntu or Debian you can install empty with apt-get install empty-expect
I actually managed to do it in python3, took a lot of work, but here is the python solution:
def record_log(output):
try:
with open(LOG_RUN_OUTPUT, 'a') as file:
file.write(output)
except:
with open(LOG_RUN_OUTPUT, 'w') as file:
file.write(output)
def execute(cmd, store=True):
proc = Popen(cmd.encode("utf8"), shell=True, stdout=PIPE, stderr=PIPE)
output = "\n".join((out.decode()for out in proc.communicate()))
template = '''Command:\n====================\n%s\nResult:\n====================\n%s'''
output = template % (cmd, output)
print(output)
if store:
record_log(output)
return output
# SSH function
def ssh_connect(start_message, host_id, user_name, key, stage_commands):
print(start_message)
try:
ssh.connect(hostname=host_id, username=user_name, key_filename=key, timeout=120)
except:
print("Failed to connect to " + host_id)
for command in stage_commands:
try:
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(command)
except:
input("Paused, because " + command + " failed to run.\n Please verify and press enter to continue.")
else:
template = '''Command:\n====================\n%s\nResult:\n====================\n%s'''
output = ssh_stderr.read() + ssh_stdout.read()
output = template % (command, output)
record_log(output)
print(output)