python seems to skip escape char "newline" while reading on stdin - python

I am using Python 3.x on Linux (Ubuntu) to read piped terminal content and save it to a database. So far everything works great! But there is a thing i dont ready understand - it seems to skip some of the newlines.
This is how i use my script (simple example) :
sudo ufw status verbose | python3 myscript.py
This is how the output direkt in terminal looks like (without pipe):
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
This is how it looks like after stdin reads it:
Status: active
Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip
This is how my function to read (from stdin) looks like :
if not sys.stdin.isatty():
ldata = ""
for line in sys.stdin:
ldata += line + "\n"
Like above shown, Python seems to skip some of the "\n". Call me pathetic, but this kind of behavior bothers me. In case of very long strings/contents this behavior makes texts almost not readable anymore.
thx in advance!

Related

python telnet not pressing enter with "\n"

I am working on a script to pull configs from Cisco devices in GNS3. Currently it is using a hard coded IP & port due to another separate issue but this is the code I am currently using.
import getpass
import telnetlib
print "Getting running-config 127.0.0.1:5002"
tn = telnetlib.Telnet("127.0.0.1","5002")
tn.write("terminal length 0\n")
tn.write("show run\n")
tn.write("exit\n")
readoutput = tn.read_until("exit")
saveoutput = open("C:\Programdata\switch.txt","w")
saveoutput.write(readoutput)
saveoutput.write("\n")
saveoutput.close
print tn.read_all()
If I comment out the readouput and saveoutput blocks of code the script runs through as fast as I would expect it to. However once I leave that code in there it just sits and hangs. I have put print() commands in to verify and it hangs on the readoutput line. I have tried both tn.read_all & tn.read_until (without and without a string parameter) and neither works.
Even though the readoutput is not working I know something is occurring because if I telnet into the switch with telnet 127.0.0.1 5002 in a command prompt it comes up without issue as well as being able to progress through my terminal commands.
Edit:
I did more testing with this today and found out through the use of a lot of sleep timers and printing to the terminal what was going on. As it turns out for some reason the telnet session is not actually pressing enter when it should. I tried changing the tn.write("text\n") to tn.write("text" + "\n")and even splitting that into two write commands, but none of it works. Below is a telnet session I opened after some failed attempts at making the script work. Note all the commands are just sitting there in line? If I change the readoutput to look for "xit" which is the text on the first line of the ios console, then it will find it in telnet and print everything in the telnet session to that point.
If anyone knows why "\n" is not actually hitting enter I would greatly appreciate the help as this should be my last major hurdle to making the program this script is a part of work.
The reason for this behaviour is because \n isn't actually carriage return (CR) - it's line feed (LF) in string, and different systems may have different EOL implementation (the most obvious example being Linux's \n versus Windows's \r\n). In case of Cisco IOS, it seems that \r is enough, although I can't currently verify it.
Try adding \r\n as this is default Telnet behaviour according to the spec, that should work.
All of your code is True and also both of :
tn.write("text\n")
tn.write("text" + "\n")
Is true. Just one missing is b :
Your code should be like this :
tn.write(b"en" + b"\n")
tn.write(b"terminal length 0\n")
tn.write(b"show run\n")
tn.write(b"exit\n")

Determine the terminal cursor position with an ANSI sequence in Python 3

I want to write a little script which prints images to the terminal with /usr/lib/w3mimgdisplay(like in mac osx lsi). Therefore i need the the actual cursor position (or caret position) when the script has started. So far i figured out to get the cursor position in a shell with an ANSI sequence:
$ echo -en "\e[6n"
^[[2;1R$ 1R
This is how the response of this ANSI sequence look like (urvxt and bash - don't know if that is important). So this sequence prints out the result (^[[2;1R) immediately. And this is what i don't understand. How is this done? If i write a very simple shell script, just with that instruction and strace the script, this doesn't clear up things ether. What the? Then i try to figure out how this happens by looking in the terminfo manpage. Can't find it here (maybe i didn't try hard enough). At this point i find myself very confused about this concept. Does the terminal write the position even to stdout?
Terminal
#!/bin/bash
echo -en "\e[6n"
$ strace sh curpos.sh
[...]
read(255, "#!/bin/bash\necho -en \"\\e[6n\"\n", 29) = 29
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
write(1, "\33[6n", 4) = 4
^[[54;21Rread(255, "", 29) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
exit_group(0) = ?
+++ exited with 0 +++
Python
First i tried to use subprocess.check_output which of course just returns the the string i've echoed. How do i capture the response to this ANSI sequence?
>>> subprocess.check_output(["echo", "-en", "\x1b[6n"])
b"\x1b[6n"
I also tried a lot of other things, like reading the stdin and stdout!? With and without threads, but all of that was more a guessing and mocking around rather than knowing what to do. I also search the Internet for quite a while, hoping to find an example of how to do this, but whit no luck. I've found this answer to the same question: https://stackoverflow.com/a/35526389/2787738 but this don't work. Actually i don't know if this has ever worked, because in this answer the ANSI sequence is written to stdout before it starts reading from stdin? Here i realised again that i don't understand the concept/mechanism how these ANSI sequences really work. So at this point every explanation which clears things a very much appreciated. The most helpful post i found was this one: https://www.linuxquestions.org/questions/programming-9/get-cursor-position-in-c-947833/. In this thread someone posted this bash script:
#!/bin/bash
# Restore terminal settings when the script exits.
termios="$(stty -g)"
trap "stty '$termios'" EXIT
# Disable ICANON ECHO. Should probably also disable CREAD.
stty -icanon -echo
# Request cursor coordinates
printf '\033[6n'
# Read response from standard input; note, it ends at R, not at newline
read -d "R" rowscols
# Clean up the rowscols (from \033[rows;cols -- the R at end was eaten)
rowscols="${rowscols//[^0-9;]/}"
rowscols=("${rowscols//;/ }")
printf '(row %d, column %d) ' ${rowscols[0]} ${rowscols[1]}
# Reset original terminal settings.
stty "$termios"
Here we can see that indeed the response is somehow magically appears one the screen :). This is why this script disables echoing on the terminal and after reading the response it resets the original terminal settings via stty.
Here is a POC snippet, how to read the current cursor position via an ansi/vt100 controll sequence.
I suggest you to put the following code inside a curpos.py file and import this file in your code (import curpos).
import re, sys, termios, tty
def getpos():
buff = ''
stdin = sys.stdin.fileno()
tattr = termios.tcgetattr(stdin)
try:
tty.setcbreak(stdin, termios.TCSANOW)
sys.stdout.write('\033[6n')
sys.stdout.flush()
while True:
buff += sys.stdin.read(1)
if buff[-1] == 'R':
break
finally:
termios.tcsetattr(stdin, termios.TCSANOW, tattr)
matches = re.match(r'^\033\[(\d*);(\d*)R', buff)
if matches == None: return None
groups = matches.groups()
return (int(groups[0]), int(groups[1]))
An example of how your code should/could be:
import curpos
res = getpos()
if res == None:
print('Problem, I cannot get the cursor possition')
else:
print(f'The cursor position is: {res}')
warning
This is not perfect. To make it more robust, a routine which sorts out keystrokes from the user while reading from stdin would be nice.

EoF / Ctrl+D command in Bash + Python

I'm trying to make some functions in python so that I can connect to a linux terminal and do stuff (like in this case, create a file). The code I have, works partially. The only thing that doesn't work is if you want to do something after you have entered the code. Like for instance you create the file and then want to navigate somewhere else (cd /tmp) for instance. Instead of doing the next command, it will just add to the file created.
def create_file(self, name, contents, location):
try:
log.info("Creating a file...")
self.device.execute("mkdir -p {}".format(location))
self.cd_path(location)
self.device.sendline("cat > {}".format(name))
self.device.sendline("{}".format(contents))
self.device.sendline("EOF") # send the CTRL + D command to save and exit I tried here with ^D as well
except:
log.info("Failed to create the file!")
The contents of the file is:
cat test.txt
#!/bin/bash
echo "Fail Method Requested"
exit 1
EOF
ls -d /tmp/asdasd
The order of commands executed is:
execute.create_file(test.txt, the_message, the_location)
execute.check_path("/tmp/adsasd") #this function just checks with ls -d if the directory exists.
I have tried with sendline the following combinations:
^D, EOF, <<EOF
I don't really understand how I could make this happen. I just want to create a file with a specific message. (When researching on how to do this with VI I got the same problem, but there the command I needed was the one for ESC)
If anyone could help with some input that would be great!!
Edit: As Rob mentioned below, sending the character "\x04" actually works. For anyone else having this issue, you can also consult this chart for other combinations if needed:
http://donsnotes.com/tech/charsets/ascii.html
You probably need to send the EOF character, which is typically CONTROL-D, not the three characters E, O, and F.
self.device.sendline("\x04")
http://wiki.bash-hackers.org/syntax/redirection#here_documents
Here docs allow you to use any file input termination string you like to represent end of file ( such as the literal EOF you're attempting to use now). Quoting that string tells the shell not to interpret expansions inside the heredoc content, ensuring that said content is treated as literal.
Using pipes.quote() here ensures that filenames with literal quotes, $s, spaces, or other surprising characters won't break your script. (Of course, you'll need to import pipes; on Python 3, by contrast, this has moved to shlex.quote()).
self.device.sendline("cat > {} <<'EOF'".format(pipes.quote(name)))
Then you can write the EOF as is, having told bash to interpret it as the end of file input.

auto run python result in "EOF when reading a line "

I have add the line into my .vimrc
map <F4> :w !python<cr>
When I open gvim to edit an unname Python file, there are only two lines in it
x=input("which one do you like ? ")
print(x)
I press F4, and get EOF when reading a line, how to solve it?
When you add map <F4> :w<cr>:!python %<cr> or imap <F4> <Esc>:w <cr>:!python %<cr>, it can only make a named file run, if it is a no named file, the map will not work, how can I make the no named file run?
#benjifisher's answer is correct. The input (function) is the problem.
The :w !python pipes the program to python through stdin (Basically the same as
echo 'input("x")' | python
which also fails if run in the shell). However input() tries to read from stdin which it can't do because python read the program from stdin and stdin is still trying to read from the pipe. However the pipe is already at the end and won't ever contain new data. So input() just reads EOF.
To see that python is reading from stdin we look at :h :w_c which shows that the file is being passed to stdin of the cmd which in this case is python.
:w_c :write_c
:[range]w[rite] [++opt] !{cmd}
Execute {cmd} with [range] lines as standard input
(note the space in front of the '!'). {cmd} is
executed like with ":!{cmd}", any '!' is replaced with
the previous command :!.
If the buffer had contained something that wasn't reading from stdin your mapping would have worked.
For example if the unnamed buffer contains
print(42)
running the command :w !python in vim prints 42.
So the problem isn't that the mapping fails. The problem is that your program doesn't know how to get input. The solution is use either a named file or don't write interactive programs in vim. Use the python interpreter for that.
Since you have :w in your mapping, I am assuming you either want to run the script directly from the insert mode or you want to save it anyways before running it.
Multiple commands in vim require a separator like <bar> i.e. (|) or use <CR> (Carriage Return) between and after commands.
You can put both of the mappings below in your .vimrc and they should meet your requirement on hitting F4, whether you are in normal mode or insert mode.
If you are in normal mode, you are fine with map:
map <F4> :w<cr>:!python %<cr>
While for insert mode, you would need imap and an Esc to get out of insert mode:
imap <F4> <Esc>:w <cr>:!python %<cr>
I think the problem is the input() line. Python is looking for input and not finding any. All it finds (wherever it looks) is an EOF.
One way to do this without a temp file, would be to do something like this(with a small helper function):
function! GetContentsForPython()
let contents = join(getline(1,'$'), "\n")
let res = ''
for l in split(contents, '\n')
if len(l)
let res = res . l . ';'
endif
endfor
let res = '"' . escape(res, '"') . '"'
return res
endfunction
noremap <f4> :!python -c <c-r>=GetContentsForPython()<cr><cr>
This gets the contents of the current buffer, and replaces the newlines with semi colons so you can execute it with
python -c "print 'hello'"
There may be better ways accomplishing this, but this seems to work for me.

How do you send part of a command to vim(in Ex mode) using if_py?

I'm trying to open a file on Ctrl-f. If the command is
typed in the presence of an empty buffer 'None' then I
want the file to be opened in that buffer, but if there
is no empty buffer I'd like to open a new buffer using
:tabnew and then open the file in that.
For this purpose I have a function OpenFile which is
invoked.
function! OpenFile()
python << EOF
import vim
import re
buffer = vim.current.buffer
name = str(buffer.name)
if re.match('None', name):
vim.command(':e ')
else:
vim.command(':tabnew')
vim.command(':e ')
EOF
endfunction
"Open file
:map <C-f> :call OpenFile()<CR>
:imap <C-f> <Esc>:call OpenFile()<CR>
vim.command executes the command so this is equivalent to
:w!ENTER What I want to do is setup part of the command..
:e FILENAME ENTER
So I want to send the :e part in Ex mode via the
python-function and get the user to type the filename
and hit ENTER
First of all, why do you write this in Python? Sure, Vimscript is a bit strange (but since Vim 7 is has become a lot like Python), and you need to learn about the integration points, anyway, and this task has very little real logic in it.
This is easiest solved via a map-expression (:help map-expression):
:noremap <expr> <C-f> empty(bufname('')) ? ':edit ' : ':tabnew '
If you must, extract the conditional into a function and code it in Python, but I would recommend sticking to Vimscript, unless the logic is really complex or you could benefit from certain libraries.

Categories