I wrote a Python script to replace "powerline" as a terminal prompt solution for myself here: https://github.com/diogobaeder/dotfiles/blob/master/.bash_prompt.py
Then all I do is to define the prompt from the output of that script:
# in my ~/.bashrc
export PS1="\$(python ~/.bash_prompt.py)"
The script itself works fine, I get the command prompt I want; However, since there's no wrapping for the styles I put there, the terminal (doesn't matter which GUI terminal program I use) doesn't calculate the prompt width correctly, and as I type characters after the prompt it ends up not wrapping them to a new line at first, overwriting the prompt completely.
Now, I know that when stylizing my bash prompt I need to escape style codes with \[ and \] so that bash takes into consideration that they're escape sequences and calculates the width correctly. However, if I put them as wrappers for my styles in my Python script (see esc_start and esc_end), I can't get them to be properly evaluated by bash as "calculation escape sequences", instead I get literal square brackets printed. If I then escape in Python the backslashes too (\\[ and \\]), then I get unescaped literals outputted (\[ and \]). Bash seems to completely ignore them as escape sequences for calculating the prompt width.
If, however, I remove the backslash in my PS1 command ($(python ~/.bash_prompt.py) instead of \$(python ~/.bash_prompt.py)), then putting \[ and \] as escape sequences in my Python script (as esc_start and esc_end), then bash considers them as proper escapes and ends up wrapping lines as expected (i.e., when I go past the right border it wraps the line as expected). The problem with this, however, is that removing this backslash from my PS1 definition in .bashrc makes the script run only once per terminal session, and not for each prompt line - so, for example, if I'm in a Git working tree and I change from one branch to another, it doesn't show the new branch as soon as the command finishes, instead it shows the old branch.
Let me give some examples of what I mean, that you can try in your own .bashrc without needing my Python script:
PS1="\[\033[31m\]\u#\h\[\033[0m\]$ " # This wraps lines correctly
PS1="\033[31m\u#\h\033[0m$ " # This makes the line overwrite the prompt
So, any ideas of how to cope with bash and make it understand the \[ and \] sequences correctly when printed by the Python script, while still keeping the script running for each command prompt? Or, if this is a limitation in bash, is there another way to force it to wrap the line when it reaches the right border of the terminal window?
Thanks!
Diogo
[EDIT] Solution (thanks, Grisha Levit!):
This is my bashrc line now:
PROMPT_COMMAND='PS1="$(python ~/.bash_prompt.py)"'
And I re-added the escapes (\[ and \]) to my script, and now it works perfectly! :-)
Bash first interprets the escape sequences in $PS1 and only afterwards handles command substitution, etc.
Bash allows these prompt strings to be customized by inserting a number of backslash-escaped special characters that are decoded as follows [...]
After the string is decoded, it is expanded via parameter expansion, command substitution, arithmetic expansion, and quote removal [...]
--Bash Reference Manual: Controlling the Prompt
This means that any special sequences printed by your command will not be interpreted as colors, etc. The solution is to use $PROMPT_COMMAND to change the value of $PS1, like:
PROMPT_COMMAND='PS1=$(python ~/.bash_prompt.py)'
Related
I want to run python code from the shell instead of from a script.
I searched everywhere and found suggestions about using echo or Shift+Enter and using ^ at the end of the line, but ^ and shift+Enter didn't work in python shell, and I need to if there is a way to separate commands in the shell.
For example I need to run this from python shell instead of from a script:
If x > y
print ("x is greater than y.")
but non of the options I tried worked.
You need to use : to issue a line separator.
i.e.
if x > y:
print ("x is greater than y.")
If you are asking how to enter Python code in your shell, then try something like
python -c 'if x > y:
print(("x is greater than y")'
assuming you are using a Bourne-compatible shell (Linux etc). This has the major drawback that your Python code cannot easily contain a single quote (though if you know the quoting rules of the shell it's of course not impossible or even unobvious how to create an expression which evaluates to a single quote).
However, the common way to do this (and perhaps the only way on Windows?) is to type the Python code into a file and then run it with
python filename.py
where filename.py is the name of the file where you saved your Python code.
You can of course run simply
python
to enter the Python interpreter in interactive mode, where you can type in Python expressions and have them evaluated immediately. When you type an expression which ends with a colon, Python changes the prompt from >>> to ... to indicate that it expects one or more indented lines. (Type just a newline immediately at the ... prompt to exit this mode.)
I have recently become the owner of a MacBook.
Due to this and my programming I have been using the terminal a lot.
I was bored so decided to mash my keyboard and ended up with an input of
agr'l
This was fine until it brought about the start of the line being > and it waiting for an input. Also at the beginning and the end of the line it has squared brackets as if they are bing held together
Any help that could be given on what this is would be appreciated.
Many Thanks
In the common Bourne compatible Unix shells (sh, ksh (Korn shell), bash, and zsh, among others) the shell will allow continuation of lines which contain unmatched quotation marks or other delimiters (such has parentheses for blocking commands with a subshell or {curly;braces} for grouping statements together). Also lines ending in a backslash \ character will be continued.
In these cases the shell will display your $PS2 prompt string (the one you're using to seeing is $PS1 ... "prompt string: primary" vs. the "prompt string: secondary." There are a couple of other $PS_ environment variable for prompting generated by a call to the select built-in and for the output lines from tracing execution of a shell script (using the set -x command, or the -x command line option to the shell).
Read the man pages and search on PS1 through PS4 to learn more about each one.
I am writing a sublime plugin in Python. But I am still very new to Python language. I am using this line to call meld in Python:
if meld_cmd == None:
meld_cmd = "C:\\Program Files (x86)\\Meld\\meld\\meld"
os.system('"%s" "%s" "%s" ' %(meld_cmd, source_name, dest_name))
It doesn't work well in Windows. The cmd window just flashes for a sec (seem to execute something) then disappears. I print the executed string out in sublime and copy the string into cmd window and execute. It executes well in both admin and non-admin mode. And I tried to add a pause before executing:
os.system('pause && "%s" "%s" "%s" ' %(meld_cmd, source_name, dest_name))
This works well too after clicking enter after the press any key to continue... line.
Not sure how to debug this and what is the reason why it is failing.
cmd.exe /c command has a parsing quirk when the command starts with a quote and has more than two quotes. The exact rules are explained in the online help text available via cmd /?:
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
To get around this, just wrap the whole command in quotes, e.g. '""%s" "%s" "%s""' % (meld_cmd, source_name, dest_name).
Probably the problem is that you need to execute that script with Admin privileges.
Proceed to question How to run python script with elevated privilege on windows to get more information on that topic.
I want to pass url to my python via the console and then do the appropriate tasks. many of the links contain the character '&' in the link. python interprets that as ending the argument this however is not what I want. here is a sample of the code
import sys
external_id = sys.argv[1].encode("utf-8")
print external_id
And when I run the following:
python graph.py 2%60&7
I get:
2%60
How do I make python interpret the '&' as nothing more than another character in the url?
This is not python, it's bash. You need to escape it:
python graph.py 2%60\&7
Quoting this answer:
The & informs the shell to put the command in the background.
Python never receives that character because the shell takes care of it, thus this isn't an issue of Python. You can also wrap 2%60&7 with quotes to make it work
me#pc:~$ python3 script.py '2%60&7'
b'2%60&7'
Sometimes escaping & is a lot harder than doing this.
Platform: Windows
Grep: http://gnuwin32.sourceforge.net/packages/grep.htm
Python: 2.7.2
Windows command prompt used to execute the commands.
I am searching for the for the following pattern "2345$" in a file.
Contents of the file are as follows:
abcd 2345
2345
abcd 2345$
grep "2345$" file.txt
grep returns 2 lines (first and second) successfully.
When I try to run the above command through python I don't see any output.
Python code snippet is as follows:
temp = open('file.txt', "r+")
grep_cmd = []
grep_cmd.extend([grep, '"2345$"' ,temp.name])
print grep_cmd
p = subprocess.Popen(grep_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdoutdata = p.communicate()[0]
print stdoutdata
If I have
grep_cmd.extend([grep, '2345$' ,temp.name])
in my python script, I get the correct answer.
The questions is why the grep command with "
grep_cmd.extend([grep, '"2345$"' ,temp.name])
executed from python fails. Isn't python supposed to execute
the command as it is.
Thanks
Gudge.
Do not put double quotes around your pattern. It is only needed on the command line to quote shell metacharacters. When calling a program from python, you do not need this.
You also do not need to open the file yourself - grep will do that:
grep_cmd.extend([grep, '2345$', 'file.txt'])
To understand the reason for the double quotes not being needed and causing your command to fail, you need to understand the purpose of the double quotes and how they are processed.
The shell uses double quotes to prevent special processing of some shell metacharacters. Shell metacharacters are those characters that the shell handles specially and does not pass literally to the programs it executes. The most commonly used shell metacharacter is "space". The shell splits a command on space boundaries to build an argument vector to execute a program with. If you want to include a space in an argument, it must be quoted in some way (single or double quotes, backslash, etc). Another is the dollar sign ($), which is used to signify variable expansion.
When you are executing a program without the shell involved, all these rules about quoting and shell metacharacters are not relevant. In python, you are building the argument vector yourself, so the relevant quoting rules are python quoting rules (e.g. to include a double quote inside a double-quoted string, prefix the double quote with a backslash - the backslash will not be in the final string). The characters in each element of the argument vector when you have completed constructing it are the literal characters that will be passed to the program you are executing.
Grep does not treat double quotes as special characters, so if grep gets double quotes in its search pattern, it will attempt to match double quotes from its input.
My original answer's reference to shell=True was incorrect - first I did not notice that you had originally specified shell=True, and secondly I was coming from the perspective of a Unix/Linux implementation, not Windows.
The python subprocess module page has this to say about shell=True and Windows:
On Windows: the Popen class uses CreateProcess() to execute the child child program, which operates on strings. If args is a sequence, it will be converted to a string in a manner described in Converting an argument sequence to a string on Windows.
That linked section on converting an argument sequence to a string on Windows does not make sense to me. First, a string is a sequence, and so is a list, yet the Frequently Used Arguments section says this about arguments:
args is required for all calls and should be a string, or a sequence of program arguments. Providing a sequence of arguments is generally preferred, as it allows the module to take care of any required escaping and quoting of arguments (e.g. to permit spaces in file names).
This contradicts the conversion process described in the Python documentation, and given the behaviour you have observed, I'd say the documentation is wrong, and only applied to a argument string, not an argument vector. I cannot verify this myself as I do not have Windows or the source code for Python lying around.
I suspect that if you call subprocess.Popen like:
p = subprocess.Popen(grep + ' "2345$" file.txt', stdout=..., shell_True)
you may find that the double quotes are stripped out as part of the documented argument conversion.
You can use python-textops3 :
from textops import *
print('\n'.join(cat('file.txt') | grep('2345$')))
with python-textops3 you can use unix-like commands with pipes within python
so no need to fork a process which is very heavy