How do you write a ESC character to be interpreted by echo? - bash

Is there a \ESC or something I can put so echo command can interpret it?
Where is a list of these commands, like \t\n\r, etc?

If you're using bash, it's probably better to use that shell's so-called "ANSI-C Quoting". This lets you construct a string that you can pass to the echo command, which will simply print it.
For example, to print an ASCII ESC character:
echo $'\e'
or
echo -n $'\e'
if you don't want the trailing newline.
(The term "ANSI-C Quoting is a bit of a misnomer for several reasons. The C standard is currently produced by ISO, not ANSI, and the bash feature supports \e and \E to represent the Escape character, and \cX for control-X, features not in standard C. The handling of hex escapes is also a bit different.)
Even better, you can use the printf command, which also supports \e for the ESC character. printf is far more portable that echo. There are a number of different versions of the echo command, both as /bin/echo and as built-in commands in most shells. printf also exists in multiple versions, but the behavior across implementations is much more consistent. Code using printf is more likely to be portable to shells other than bash (or to older versions of bash) than code that depends on any of the more arcane features of bash's echo command, or of the GNU coreutils echo command.

Yes, you might have to pass the -e switch:
echo -e '\n\t\a'
you can find the list of interpreted escapes in echo manpage and info entry. This is for GNU coreutils echo, likely the one you are using:
If -e is in effect, the following sequences are recognized:
\\ backslash
\a alert (BEL)
\b backspace
\c produce no further output
\e escape
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\0NNN byte with octal value NNN (1 to 3 digits)
\xHH byte with hexadecimal value HH (1 to 2 digits)
Bash will replace /bin/echo with its own builtin, but all sequences above are interpreted.
Note that Posix compliance does not require this, so some shells will have partial or no support for the -e flag and escaping, namely Debian/Ubuntu Dash, which is the default shell on those platforms. You will have to invoke echo as /bin/echo to avoid calling the builtin and make sure your /bin/echo can handle escapes.
Use printf if you need to write portable code.

Related

Escape ANSI sequence of non-printing characters between \[ and \]

The bash manual says that, in the prompt, any sequences of non-printing characters should be enclosed like: \[this\]:
\[ Begin a sequence of non-printing characters.
This could be used to embed a terminal control sequence into the prompt.
\] End a sequence of non-printing characters.
Given a string to be included in the prompt, how can i automatically escape all ANSI control / color codes, to make the prompt display / wrap correctly under all circumstances?
Differentiation: Here i assume that a string with ANSI control codes has already been produced.
This related question assumes that the delimiters can be inserted by editing the string's generating function.
The following will enclose ANSI control sequences in ASCII SOH (^A) and STX (^B) which are equivalent to \[ and \] respectively:
function readline_ANSI_escape() {
if [[ $# -ge 1 ]]; then
echo "$*"
else
cat # Read string from STDIN
fi | \
perl -pe 's/(?:(?<!\x1)|(?<!\\\[))(\x1b\[[0-9;]*[mG])(?!\x2|\\\])/\x1\1\x2/g'
}
Use it like:
$ echo $'\e[0;1;31mRED' | readline_ANSI_escape
Or:
$ readline_ANSI_escape "$string"
As a bonus, running the function multiple times will not re-escape already escaped control codes.
Don't try to automate it.
The reason why Bash asks you to add the \[...\]'s manually is because the shell can't reasonably know how any given terminal will interpret any escape codes. If it was, Bash would just do it in the first place.
For example, here are a few of the many cases that the other answer fails to handle. In each case, no output is printed on my particular terminal and yet the escaping function fails to escape them:
printf '\e]1;Hello\a' # Set window title
printf '\e[0;2;0;0;0}' # 24-bit color
printf '\e[13]' # Unblank screen

Prevent "echo" from interpreting backslash escapes

I'd like to echo something to a file that contains new line escape sequences, however I would like them to remain escaped. I'm looking for basically the opposite to this question.
echo "part1\npart2" >> file
I would like to look like this in the file
$ cat file
old
part1\npart2
but it looks like
$ cat file
old
part1
part2
This is a good example of why POSIX recommends using printf instead of echo (see here, under "application usage"): you don't know what you get with echo1.
You could get:
A shell builtin echo that does not interpret backslash escapes by default
Example: the Bash builtin echo has an -e option to enable backslash escape interpretation and checks the xpg_echo shell option
A shell builtin echo that interprets backslash escapes by default
Examples: zsh, dash
A standalone executable /bin/echo: probably depends on which one – GNU Coreutils echo understands the -e option, like the Bash builtin
The POSIX spec says this (emphasis mine):
The following operands shall be supported:
string
A string to be written to standard output. If the first operand is -n, or if any of the operands contain a <backslash> character, the results are implementation-defined.
So, for a portable solution, we can use printf:
printf '%s\n' 'part1\npart2' >> file
where the \n in the format string will always be interpreted, and the \n in the argument will never be interpreted, resulting in
part1\npart2
being appended to file.
1 For an exhaustive overview of various behaviours for echo and printf, see echo(1) and printf(1) on in-ulm.de.

removing backslash with tr

So Im removing special characters from filenames and replacing with spaces. I have all working apart from files with single backslashes contained therein.
Note these files are created in the Finder on OS X
old_name="testing\this\folder"
new_name=$(echo $old_name | tr '<>:\\#%|?*' ' ');
This results in new_name being "testing hisolder"
How can I just removed the backslashes and not the preceding character?
This results in new_name being "testing hisolder"
This string looks like the result of echo -e "testing\this\folder", because \t and \f are actually replaced with the tabulation and form feed control characters.
Maybe you have an alias like alias echo='echo -e', or maybe the implementation of echo in your version of the shell interprets backslash escapes:
POSIX does not require support for any options, and says that the
behavior of ‘echo’ is implementation-defined if any STRING contains a
backslash or if the first argument is ‘-n’. Portable programs can use
the ‘printf’ command if they need to omit trailing newlines or output
control characters or backslashes.
(from the info page)
So you should use printf instead of echo in new software. In particular, echo $old_name should be replaced with printf %s "$old_name".
There is a good explanation in this discussion, for instance.
No need for printf
As #mklement0 suggested, you can avoid the pipe by means of the Bash here string:
tr '<>:\\#%|?*' ' ' <<<"$old_name"
Ruslan's excellent answer explains why your command may not be working for you and offers a robust, portable solution.
tl;dr:
You probably ran your code with sh rather than bash (even though on macOS sh is Bash in disguise), or you had shell option xpg_echo explicitly turned on.
Use printf instead of echo for portability.
In Bash, with the default options and using the echo builtin, your command should work as-is (except that you should double-quote $old_name for robustness), because echo by default does not expand escape sequences such as \t in its operands.
However, Bash's echo can be made to expand control-character escape sequences:
explicitly, by executing shopt -s xpg_echo
implicitly, if you run Bash as sh or with the --posix option (which, among other options and behavior changes, activates xpg_echo)
Thus, your symptom may have been caused by running your code from a script with shebang line #!/bin/sh, for instance.
However, if you're targeting sh, i.e., if you're writing a portable script, then echo should be avoided altogether for the very reason that its behavior differs across shells and platforms - see Ruslan's printf solution.
As an aside: perhaps a more robust approach to your tr command is a whitelisting approach: stating only the characters that are explicitly allowed in your result, and excluding other with the -C option:
old_name='testing\this\folder'
new_name=$(printf '%s' "$old_name" | tr -C '[:alnum:]_-' ' ')
That way, any characters that aren't either letters, numbers, _, or - are replaced with a space.
With Bash, you can use parameter expansion:
$ old_name="testing\this\folder"
$ new_name=${old_name//[<>:\\#%|?*]/ }
$ echo $new_name
testing this folder
For more, please refer to the Bash manual on shell parameter expansion.
I think your test case is missing proper escaping for \, so you're not really testing the case of a backslash contained in a string.
This worked for me:
old_name='testing\\this\\folder'
new_name=$(echo $old_name | tr '<>:\\#%|?*' ' ');
echo $new_name
# testing this folder

What's the best way to embed a Unicode character in a POSIX shell script?

There's several shell-specific ways to include a ‘unicode literal’ in a string. For instance, in Bash, the quoted string-expanding mechanism, $'', allows us to directly embed an invisible character: $'\u2620'.
However, if you're trying to write universally cross-platform shell-scripts (generally, this can be truncated to “runs in Bash, Zsh, and Dash.”), that's not a portable feature.
I can portably achieve anything in the ASCII table (octal number-space) with a construct like the following:
WHAT_A_CHARACTER="$(printf '\036')"
… however, POSIX / Dash printf only supports octal escapes.
I can also obviously achieve the full Unicode space by farming the task out to a fuller programming environment:
OH_CAPTAIN_MY_CAPTAIN="$(ruby -e 'print "\u2388"')"
TAKE_ME_OUT_TONIGHT="$(node -e 'console.log("\u266C")')"
So: what's the best way to encode such a character into a shell-script, that:
Works in dash, bash, and zsh,
shows the hexadecimal encoding of the codepoint in the code,
isn't dependant on the particular encoding of the string (i.e. not by encoding UTF-8 bytes in octal)
and finally, doesn't require the invocation of any “heavy” interpreter. (let's say, less than 0.01s runtime.)
If you have Gnu printf installed (it's in debian package coreutils, for example), then you can use it independent of which shell you are using by avoiding the shell's builtin:
env printf '\u2388\n'
Here I am using the Posix-standard env command to avoid the use of the printf builtin, but if you happen to know where printf is you could do this directly by using the complete, path, such as
/usr/bin/printf '\u2388\n'
If both your external printf and your shell's builtin printf only implement the Posix standard, you need to work harder. One possibility is to use iconv to translate to UTF-8, but while the Posix standard requires that there be an iconv command, it does not in any way prescribe the way standard encodings are named. I think the following will work on most Posix-compatible platforms, but the number of subshells created might be sufficient to make it less efficient than a "heavy" script interpreter:
printf $(printf '\\%o' $(printf %08x 0x2388 | sed 's/../0x& /g')) |
iconv -f UTF-32BE -t UTF-8
The above uses the printf builtin to force the hexadecimal codepoint value to be 8 hex digits long, then sed to rewrite them as 4 hex constants, then printf again to change the hex constants into octal notation and finally another printf to interpret the octal character constants into a four-byte sequence which can be fed into iconv as big-endian UTF-32. (It would be simpler with a printf which recognizes \x escape codes, but Posix doesn't require that and dash doesn't implement it.)
You can use the line without modification to print more than one symbol, as long as you provide the Unicode codepoints (as integer constants) for all of them (example executed in dash):
$ printf $(printf '\\%o' $(printf %08x 0x2388 0x266c 0xA |
> sed 's/../0x& /g')) |
> iconv -f UTF-32BE -t UTF-8
⎈♬
$
Note: As Geoff Nixon mentions in a comment, the fish shell (which is nowhere close to Posix standard, and as far as I can see has no aspirations to conform) will complain about the unquoted %08x format argument to printf, because it expects words starting with % to be jobspecs. So if you use fish, add quotes to the format argument.
i would go with
echo -e "\xc3\xb6"
do check it:
~ $ echo -e "\xc3\xb6"
ö
~ $ echo -n ö | hexdump
0000000 b6c3
0000002

How to enter a tab char on command line?

In an interactive bash terminal how do I enter a tab character? For example, if I wanted to use sed to replace "_" with tabs I'd like to use:
echo $string | sed 's/[_]/TAB/g'
Where TAB means the tab key. This works in a shell script not interactively where when I hit the tab key I get no character and a clank noise sounds. I've also tried \t but it only places t's in the string and not tabs.
Note this is mac osx.
Precede it with Control + V, followed by Tab to suppress the usual expansion behavior.
Since this question is tagged "bash"... using the "ANSI-C quoting" feature of the Bash shell is probably preferable. It expands ANSI C-style escape sequences such as \t and \n and returns the result as a single-quoted string.
echo $string | sed $'s/_/\t/g'
This does not rely on your terminal understanding the Control+V (insert next character literally) key binding—some may not. Also, because all the "invisible" characters can be represented literally, your solution can be copy-pasted without loss of information, and will be much more obvious/durable if you're using including this sed invocation in a script that other people might end up reading.
Also note that macOS's version of sed is the BSD version. GNU sed will interpret character escapes like \t in the replacement pattern just fine, and you wouldn't even need above workaround.
You can install GNU sed with MacPorts, and it should be made available as gsed, but Homebrew might supersede your system's sed depending on how you have your $PATH variable arranged.

Resources