What's going on with tr(anslate) here? - shell

In applescript, if I do:
do shell script "echo \"G:\\CRE\\MV Studios\\Exhibition Projects\"|tr \"\\\\\" \"/\""
I'd expect all my backslashes to come back as forward slashes. To make it slightly easier to understand, the tr command would look like this without all the escapes
tr "\\" "/" #there's still an escaped \ for the shell
But what I get is:
"G:/CRE/MV Studiosxhibition Projects"
Note that when I copied that from Script Editor it added a weird character where the missing /E should be, it doesn't show up in the event log or once I've posted this. Obviously it's doing something weird with \E.
Any ideas on what to do about it?

It appears that echo is interpreting \E as an escape character (ASCII code 27, ESC). You can disable this with the echo -E option to disable interpretation of escape sequences.
From help echo on my Mac:
echo: echo [-neE] [arg ...]
Output the ARGs. If -n is specified, the trailing newline is
suppressed. If the -e option is given, interpretation of the
following backslash-escaped characters is turned on:
\a alert (bell)
\b backspace
\c suppress trailing newline
\E escape character
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\0nnn the character whose ASCII code is NNN (octal). NNN can be
0 to 3 octal digits
You can explicitly turn off the interpretation of the above characters
with the -E option.

Related

Tripple backslash in Bash 'echo -e' is acting strange

I have multiple Bash variables for ANSI terminal colors. One is ANSI_NOCOLOR and defined as this:
ANSI_NOCOLOR="\e[0m"
When I use it togather with a backslash character \ (escaped as \\ in Bash strings), I get an unexpected output.
Example:
echo -e "command --with --many --options \\$ANSI_NOCOLOR"
echo -e "--more --options"
This results in:
command --with --many --options \e[0m
--more --options
This example can be reduced to:
$ echo -e "\\\e[0m"
\e[0m
Why is a tripple backslash in Bash not acting as normally known from other C-like languages?
Expected / C-like behavior:
Escape sequences are left-associative. Thus,
the first two \\ are printed as \,
the remaining \ is doing a look-ahead(1) to find e for creating the ESC character.
Workaround:
After some planing with backslashes, I found, that 5 !! backslashes are required. I still would like to read an explanation, why it behaves as it is.
$ echo -e "\\\\\e[33mfoo\e[0m"
\foo
It's hard to control a color-reset sequence, so my workaround uses two ANSI color escape sequences, to set it to yellow and back to default.
This happens because there are two levels of escaping at work:
escaping for the double quoted string. This pass recognizes \\ but not \e
echo -e escape sequences. This pass recognizes both \\ and \e.
So:
The initial string is \\\e
The double quotes replace the \\ but leaves the unsupported \e alone
You now have \\e
Echo replaces the \\
You now have \e
This is one of the many reasons why you should prefer printf over echo whenever any value may contain a backslash.

Why the `\b` do not work when it as the last character in echo?

When I test bash script on my Mac, you know the \b is the DEL function:
$ echo -e "\\ 2\b"
\ 2
$ echo -e "\\ 2\b123"
\ 123
$ echo -e "\\ 2\b "
\
I found if the \b as the last character it will not work, see the first case.
But if there is space or other character following it, it will work.
EDIT
Sorry, \b not DEL, it is backspace, thanks #Evert
\b does not represent DEL, it represents backspace. This moves the Terminal cursor backward one space, but does not do anything to the character that's been backspaced over. If you print something after the backspace character, that'll be written over the previous character (effectively deleting it), but if you don't, it just remains on screen. If you want to really delete the character, send backspace+space+backspace (i.e. "\b \b").
BTW, echo isn't a good way to print sequences like this. Some versions convert escape sequences to the characters they represent, but some just print the escapes literally. I had to rewrite a bunch of my scripts when Mac OS X v10.5 came out, and had different behavior than I expected. Since then, I've always preferred printf for anything nontrivial. It's a bit more complicated than echo, because the first argument is treated as a format string, which is used to control how the rest of its arguments are printed. Also, it doesn't automatically add a newline at the end like echo does, so if you want that, you have to add it manually (with \n).
Finally, I'd use single-quotes for strings like this that include escape sequences, because in double-quotes some escape sequences get interpreted by the shell before being sent to the command (whether it's echo or printf). So here's how I'd do your second example:
printf '\\ 2\b123\n'

Rewriting commands from history causes pieces of command and PS1 to be deleted and cursor to move unexpectedly [duplicate]

I'm using custom bash prompt to show git branch.
Everything is in /etc/bash/bashrc:
function formattedGitBranch {
_branch="$(git branch 2>/dev/null | sed -e "/^\s/d" -e "s/^\*\s//")"
# tried these:
echo -e "\e[0;91m ($_branch)"
echo -e "\e[0;91m ($_branch) \e[m"
echo -e $'\e[0;91m'"($_branch)"
echo "($_branch)"
echo "$(tput setaf 2) ($_branch) $(tput setaf 9)"
printf "\e[0;91m ($_branch)"
}
# color is set before function call
PS1='\[\033[01;34m\] \[\033[0;91m\]$(formattedGitBranch) \$\[\033[00m\] '
# color is set inside function
PS1='\[\033[01;34m\] $(formattedGitBranch) \$\[\033[00m\] '
Problem is that when I set color for $_branch in the function, my prompt will be overwritten when EOL is reached:
Tried all possible variants tput, printf, $'' notation.
I solved the problem by setting the colour only in PS1:
But..
I would like to know why it is overwriting my prompt
How to fix this issue when function is used
I'm using Gentoo Linux. GNU bash, verze 4.2.37(1)-release (i686-pc-linux-gnu)
1) I would like to know why it is overwriting my prompt
Because every non-printable characters have to be escaped by \[ and \] otherwise readline cannot keep track of the cursor position correctly.
You must put \[ and \] around any non-printing escape sequences in your prompt.
Without the \[ \] bash will think the bytes which constitute the escape sequences for the color codes will actually take up space on the screen, so bash won't be able to know where the cursor actually is.
\[ Begin a sequence of non-printing characters. (like color escape sequences). This
allows bash to calculate word wrapping correctly.
\] End a sequence of non-printing characters.
-- BashFAQ
...note the escapes for the non printing characters, these ensure that readline can keep track of the cursor position correctly. -- ss64.com
2) How to fix this issue when function is used
If you want to set colours inside a function whose output is used in PS you have two options.
Either escape the whole function call:
PS1='\[ $(formattedGitBranch) \] '
Or replace the non-printing Escape sequences inside echo. That is, replace:
\[ and \] with \001 \002
(thanks to user grawity!)
echo -e is not aware of bash's \[ \] so you have to substitute these with \001 & \002 ASCII control codes to delimit non-printable chars from printable:
function formattedGitBranch { echo -e "\001\e[0;91m\002 ($_branch)"; }
PS1='$(formattedGitBranch) '
Strings like \e[0;91m needs additional quoting, to prevent bash from calculating its length.
Enclose these strings from formattedGitBranch in \[ & \] as, \[\e[0;91m\]
You have done it correctly in other places. Just missed it in formattedGitBranch.
You have to take care of non printable character inside [\ and ] otherwise you might be getting cursor right on top of command prompt as shared in question itself , so I found something and just sharing it :-
For getting cursor after PS1 output on the same line :
few examples :
PS1='[\u#\h:\w]\$
PS1='[\[\033[0;32m\]\u#\h:\[\033[36m\]\W\[\033[0m\]]\$ '
Refer Link : syntax for bash PS1

Custom Bash prompt is overwriting itself

I'm using custom bash prompt to show git branch.
Everything is in /etc/bash/bashrc:
function formattedGitBranch {
_branch="$(git branch 2>/dev/null | sed -e "/^\s/d" -e "s/^\*\s//")"
# tried these:
echo -e "\e[0;91m ($_branch)"
echo -e "\e[0;91m ($_branch) \e[m"
echo -e $'\e[0;91m'"($_branch)"
echo "($_branch)"
echo "$(tput setaf 2) ($_branch) $(tput setaf 9)"
printf "\e[0;91m ($_branch)"
}
# color is set before function call
PS1='\[\033[01;34m\] \[\033[0;91m\]$(formattedGitBranch) \$\[\033[00m\] '
# color is set inside function
PS1='\[\033[01;34m\] $(formattedGitBranch) \$\[\033[00m\] '
Problem is that when I set color for $_branch in the function, my prompt will be overwritten when EOL is reached:
Tried all possible variants tput, printf, $'' notation.
I solved the problem by setting the colour only in PS1:
But..
I would like to know why it is overwriting my prompt
How to fix this issue when function is used
I'm using Gentoo Linux. GNU bash, verze 4.2.37(1)-release (i686-pc-linux-gnu)
1) I would like to know why it is overwriting my prompt
Because every non-printable characters have to be escaped by \[ and \] otherwise readline cannot keep track of the cursor position correctly.
You must put \[ and \] around any non-printing escape sequences in your prompt.
Without the \[ \] bash will think the bytes which constitute the escape sequences for the color codes will actually take up space on the screen, so bash won't be able to know where the cursor actually is.
\[ Begin a sequence of non-printing characters. (like color escape sequences). This
allows bash to calculate word wrapping correctly.
\] End a sequence of non-printing characters.
-- BashFAQ
...note the escapes for the non printing characters, these ensure that readline can keep track of the cursor position correctly. -- ss64.com
2) How to fix this issue when function is used
If you want to set colours inside a function whose output is used in PS you have two options.
Either escape the whole function call:
PS1='\[ $(formattedGitBranch) \] '
Or replace the non-printing Escape sequences inside echo. That is, replace:
\[ and \] with \001 \002
(thanks to user grawity!)
echo -e is not aware of bash's \[ \] so you have to substitute these with \001 & \002 ASCII control codes to delimit non-printable chars from printable:
function formattedGitBranch { echo -e "\001\e[0;91m\002 ($_branch)"; }
PS1='$(formattedGitBranch) '
Strings like \e[0;91m needs additional quoting, to prevent bash from calculating its length.
Enclose these strings from formattedGitBranch in \[ & \] as, \[\e[0;91m\]
You have done it correctly in other places. Just missed it in formattedGitBranch.
You have to take care of non printable character inside [\ and ] otherwise you might be getting cursor right on top of command prompt as shared in question itself , so I found something and just sharing it :-
For getting cursor after PS1 output on the same line :
few examples :
PS1='[\u#\h:\w]\$
PS1='[\[\033[0;32m\]\u#\h:\[\033[36m\]\W\[\033[0m\]]\$ '
Refer Link : syntax for bash PS1

How can I split a shell command over multiple lines when using an IF statement?

How can I split a command over multiple lines in the shell, when the command is part of an if statement?
This works:
if ! fab --fabfile=.deploy/fabfile.py --forward-agent --disable-known-hosts deploy:$target; then rc=1
fi
This doesn't work:
# does not work:
if ! fab --fabfile=.deploy/fabfile.py \
--forward-agent \
--disable-known-hosts deploy:$target; then
rc=1
fi
Instead of the whole command executing, I get:
./script.sh: line 73: --forward-agent: command not found
More importantly, what is missing from my understanding of Bash that will help me understand this and similar issues in the future?
The line-continuation will fail if you have whitespace (spaces or tab characters¹) after the backslash and before the newline. With no such whitespace, your example works fine for me:
$ cat test.sh
if ! fab --fabfile=.deploy/fabfile.py \
--forward-agent \
--disable-known-hosts deploy:$target; then
echo failed
else
echo succeeded
fi
$ alias fab=true; . ./test.sh
succeeded
$ alias fab=false; . ./test.sh
failed
Some detail promoted from the comments: the line-continuation backslash in the shell is not really a special case; it is simply an instance of the general rule that a backslash "quotes" the immediately-following character, preventing any special treatment it would normally be subject to. In this case, the next character is a newline, and the special treatment being prevented is terminating the command. Normally, a quoted character winds up included literally in the command; a backslashed newline is instead deleted entirely. But otherwise, the mechanism is the same. Most importantly, the backslash only quotes the immediately-following character; if that character is a space or tab, you just get a literal space or tab; the backslash will have no effect on a subsequent newline.
¹ or carriage returns, for that matter, as Czechnology points out. The POSIX shell does not get along with Windows-formatted text files, not even in WSL. Or Cygwin, but at least their Bash port has added an igncr option that you can set -o to make it carriage-return-tolerant.
For Windows/WSL/Cygwin etc users:
Make sure that your line endings are standard Unix line feeds, i.e. \n (LF) only.
Using Windows line endings \r\n (CRLF) line endings will break the command line break.
This is because having \ at the end of a line with Windows line ending translates to
\ \r \n.
As Mark correctly explains above:
The line-continuation will fail if you have whitespace after the backslash and before the newline.
This includes not just space () or tabs (\t) but also the carriage return (\r).

Resources