os.system() and subprocess.Popen() kill escape sequences in Python - python

I use libtmux and libtmux uses subprocess.Popen() to invoke Tmux to control session, windows and panes. To set a pane titel Tmux requires to send an escape sequens. This works on command line as expected:
tmux send-keys -t 0 " printf '\033]2;%s\033\\' 'Pane Title'" C-m
When this command is issued by subprocess.Popen() or os.system() - may be others too, I've only tested these two - the escape squence does not make it to the shell. I logg the command sent to a file and the values in the log file are correct but when sent only this survives:
printf '2;%s' 'Pane Title'
I've tested this by executing this command:
echo "printf '\033]2;%s\033\\' 'Pane Title'" > /tmp/setname
The content of /tmp/setname is the above.
The methode finally used is tmux_cmd and I call it via send_keys like this:
p.send_keys("printf '\033]2;%s\033\\' '" + names[i] + "'")
where "p" is the pane object. See my post here.
My question is: How to issue shell commands with escape sequences in Python?

"raw prefixes" was what I've missed! Thanks Jean-François Fabre!
This works:
p.send_keys(r"printf '\033]2;%s\033\\' '" + names[i] + "'")

Related

How to use input() function of Python in bash script?

I am trying to integrate a Python script into a bash script. However when I use the input() function, I am getting an EOFError. How can I fix this problem?
#!/bin/bash
python3 <<END
print(input(">>> "))
END
You cannot source both the script and the user input through the program's standard input. (That's in effect what you're trying to do. << redirects the standard input.)
Ideally, you would provide the script as command line argument instead of stdin using -c SCRIPT instead of <<EOF heredoc EOF:
#!/bin/bash
python3 -c 'print(input(">>> "))'
Note that you may need to mind your quoting and escaping in case you have a more complicated Python script with nested quotes.
You can still let the script run over multiple lines, if you need to:
#!/bin/bash
python3 -c '
import os.path
path_name = input("enter a path name >>> ")
file_exists = os.path.exists(path_name)
print("file " + path_name + " " +
("exists" if file_exists else "does not exist"))
'
Note that you will get into trouble when you want to use single quotes in your Python script, as happens when you want to print doesn't instead of does not.
You can work around that using several approaches. The one I consider most flexible (apart from putting you into quoting hell) is surrounding the Python script with double quotes instead and properly escape all inner double quotes and other characters that the shell interprets:
#!/bin/bash
python3 -c "
print(\"It doesn't slice your bread.\")
print('But it can', 'unsliced'[2:7], 'your strings.')
print(\"It's only about \$0. Neat, right?\")
"
Note that I also escaped $, as the shell would otherwise interpret it inside the surrounding double quotes and the result may not be what you wanted.

Changing files on remote machine using Paramiko

I'm trying to change a file on remote Linux vm, and redirect it's output to another one (on the same machine) using Paramiko. It should be simple, but I'm obviously missing something here. I use tr command to replace every blank space for tab then redirect it to another file:
command= "tr ' ' '\t' <file1 .txt> file2.txt"
This command actually works fine when executed inside shell, but when I send it over SSH:
(stdin, stdout, stderr) = ssh.exec_command(command)
It creates an empty file, and redirects its output to stdout. Can anyone give me an explanation on why is this happening? I tried to look in documentation, but I couldn't find the solution.
You should put quotes around your command:
command='tr " " "\t" < /tmp/file1.txt > /tmp/file2.txt'

using apostrophe with python's subprocess

seems like I can't use apostrophe, the command fails with no informative error.
I'm trying to execute the following:
secretLsCmd = subprocess.Popen(('docker', 'secret', 'ls') , stdout=subprocess.PIPE)
oneWhitespaceCmd = subprocess.Popen(('tr', '-s','" "') , stdout=subprocess.PIPE, stdin=secretLsCmd.stdout)
onlySecretsCmd = subprocess.check_output(('cut', "-d' '", '-f2') , stdin=oneWhitespaceCmd.stdout)
in a normal Linux terminal, it would execute the following command:
docker secret ls | tr -s " " | cut -d' ' -f2
Running this command in CLI works fine, but once I put it in python it isn't working. The 2 first commands in the pipe are working fine (i have checked), the last command is not working, exiting with error code 1... I'm almost 100% sure it is the -d' ' , but how can I fix that? any idea?
This line:
oneWhitespaceCmd = subprocess.Popen(('tr', '-s','" "'), ...)
is actually running:
tr -s '" "'
so you want to lose the extra double quotes there: Python will quote any arguments that it need to quote for the shell.
This command:
onlySecretsCmd = subprocess.check_output(('cut', "-d' '", '-f2'), ...)
is equivalent to the shell command:
cut '-d'"'"' '"'"'' -f2
so again, you probably just want to lose the quotes round the whitespace:
onlySecretsCmd = subprocess.check_output(('cut', "-d ", '-f2'), ...)
and leave it to Python to insert quotes where required. This will actually run (what should be equivalent to what you want though not identical):
cut '-d ' -f2
I used shlex.quote() to create a shell equivalent to the commands you are running, though in practice unless you tell subprocess to invoke a shell it will just be executing the equivalent of the command bash would execute after parsing all the escape quote marks. So internally the escaping isn't happening but the quotes to distinguish the arguments aren't needed either.

Bash in Python: Delete single quote

I'm executing a shell command in python but when it comes to execute this one in python (which deletes from a text file this symbol ' sigle quote) it doesn't work at all. Please could yout help me with it.
Command:
commands.getoutput('tr -d "'" < /tmp/file_1.txt > /tmp/file_2.txt')
P.S.
The shell command executed in a terminal does work.
Thanks
A syntax highlighter should already show you the problem. You need to escape the single quote inside the command string:
commands.getoutput('tr -d "\'" < /tmp/file_1.txt > /tmp/file_2.txt')

Disable 'pause' in windows bat script

In windows, I am running a bat script that currently ends with a 'pause' and prompts for the user to 'Press any key to continue...'
I am unable to edit the file in this scenario and I need the script to terminate instead of hang waiting for input that will never come. Is there a way I can run this that will disable or circumvent the prompt?
I have tried piping in input and it does not seem to help. This script is being run from python via subprocess.Popen.
Try to execute cmd.exe /c YourCmdFile < nul
YourCmdFile - full path to your batch script
subprocess.call("mybat.bat", stdin=subprocess.DEVNULL)
Would call mybat.bat and redirect input from nul on windows (which disables pause as shown in other answers)
This one turned out to be a bit of a pain. The redirect of nul from Maximus worked great, thanks!
As for getting that to work in python, it came down to the following. I started with:
BINARY = "C:/Program Files/foo/bar.exe"
subprocess.call([BINARY])
Tried to add the redirection but subprocess.call escapes everything too well and we loose the redirect.
subprocess.call([BINARY + " < nul"])
subprocess.call([BINARY, " < nul"])
subprocess.call([BINARY, "<", "nul"])
Using shell=True didn't work because the space in BINARY made it choke trying to find the executable.
subprocess.call([BINARY + " < nul"], shell=True)
In the end, I had to resort back to os.system and escape myself to get the redirection.
os.system(quote(BINARY) + " < nul")
Not ideal, but it gets the job done.
If anyone knows how to get the last subprocess example to work with the space in the binary, it would be much apprecated!
For me the following code works:
p = Popen("c:\script.bat <nul", cwd="c:\")

Categories