I need to identify whether my terminal prints colored output or not using bash scripts. Is there any direct shell command to do this? How can I do this?
My main goal is to identify whether the output matches with the default font color of terminal or not. If it's not matching, I should write an alert message in a text file.
Control characters are output characters as well, so you can detect their sequences similar to this answer.
if printf "\x1b[31mRed\x1b[0m" | grep -Pq "\\x1b\[[0-9;]+m"; then
echo colored
else
echo uncolored
fi
printf supports outputting control sequences. If you use echo, you'll need -e.
grep -q will suppress printing and just exit 0 (success) if it finds a match, and nonzero (failure) if it doesn't.
You'll need -P (PERL regular expressions) on grep for it to interpret the control sequence, because POSIX regular expressions don't support escape characters. Note the double-backslash in \\x1b, meaning you're letting grep handle the escape instead of your shell. Perl regular expressions are supported in GNU grep but seem not to be supported in BSD (including Mac OS X).
Some scripts only use control characters if they detect the input is tty-like, so you may want to use the script command to capture output including control characters directly to a file.
The way I do it is by searching for the
Escape Sequence, followed
by the color code, example
# red
awk '/\033\[31m/'
Example
Related
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
I have a funny issue with grep. Basically, I am trying to match certain control characters in a file and get the count.
grep -ocbUaE $"\x07\|\x08\|\x0B\|\x0C\|\x1A\|\x1B" <file>
Funny enough, in CLI it matches all control characters and returns the correct count, but if I use it in a bash script, it doesn't match anything.
Any ideas what I am doing wrong?
Tested on: MacOS and CentOS - same issue.
Thank you for your help!
I think you should change your command to:
grep -cUaE $'[\x07\x08\x0B\x0C\x1A\x1B]' file
I removed the extra output flags, which get ignored when -c is present. I assume that you include -U and -a for a reason.
The other changes are to use $'' with single quotes (you don't want a double-quoted string here), and replace your series of ORs with a bracket expression, which matches if any one of the characters match.
Note that C-style strings $'' don't work in all shells, so if you want to use bash you should call your script like bash script.sh and/or include the shebang #!/bin/bash if it is executable. sh script.sh does not behave in the same way as bash script.sh.
I have an applescript
do shell script "echo -n -e \\\\x61\\\\x61\\\\x61 > /tmp/file.txt"
But the file.txt does not contain "aaa"!
It contains "-n -e aaa\n" instead.
Can someone help me with that problem?
Different versions of echo are hopelessly inconsistent in how they interpret command options (like -n and -e) and/or escape sequences in the string. It's not just bash vs. sh as cdarke said; it's much messier than that. The best thing to do is just avoid either one by using printf instead. It's a bit more complicated to use than echo, but completely worth it because your scripts won't break just because the latest version of the shell was compiled with different options(!).
In this case, using printf is actually even simpler than using echo, because it always interprets escape sequences (in its first argument, the "format string" -- the rest are different), and doesn't print a newline at the end (unless you explicitly tell it to with \n at the end of the format string). So your script becomes:
do shell script "printf \\\\x61\\\\x61\\\\x61 > /tmp/file.txt"
...although you can simplify it further by using single-quotes to keep the shell from interpreting escapes before they get to printf:
do shell script "printf '\\x61\\x61\\x61' > /tmp/file.txt"
(The escapes are still doubled, because they're being interpreted by AppleScript. But at least they don't need to be quadrupled anymore.)
(p.s. relevant xkcd)
I found this example:
echo -e "This is red->\e[00;31mRED\e[00m"
It works if execute direct, from command line, bu if create file like:
#! /usr/bin/sh
echo -e "This is red->\e[00;31mRED\e[00m"
Doesn't work. How to fix? Or may be possible output in bold?
Please don't use Lua it doesn't installed.
Edit This might be your problem (likely):
#!/bin/bash
echo -e "This is red->\e[00;31mRED\e[00m"
The reason is that sh doesn't have a builtin echo command, that supports escapes.
Alternatively you might invoke your script like
bash ./myscript.sh
Backgrounders
ANSI escape sequences are interpreted by the terminal.
If you run in a pipe/with IO redirected, ouput won't be to a terminal, hence the escapes don't get interpreted.
Hints:
see ansifilter for a tool that can filter ANSI escape sequences (and optionally translate to HTML and others)
use GNU less, e.g. to get ANSI escapes working in a pager:
grep something --colour=always files.* | less -R
Or simply, as I do
# also prevent wrapping long lines
alias less='less -SR'
Use an echo program, not an echo built-in command:
#!/bin/sh
MYECHO="`which echo`"
if <test-whether-MYECHO-empty-and-act-accordingly> ...
...
$MYCHO -e "This is red->\e[00;31mRED\e[00m"
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.