bash command preserve color when piping [duplicate] - bash

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Can colorized output be captured via shell redirect?
setup
In this case specifically I'm trying to preserve the colors in git status -s when piping it to another command.
Some git commands, diff for instance, and other commands like grep have an option --color=always but git status does not.
question
Is there a way to pipe or capture the output of a command and make it think it is outputting to the xterm shell so it doesn't automatically disable colors?

Here's a script snippet using the colorized output of ls as an example (on Mac OS X 10.6).
# no colored ls output if stdout is a pipe (and not a tty)
ls -G /
ls -G / | cat
script -q /dev/null ls -G / | tr -d '\r' | cat
# write output of script command to a variable
var="$(script -q /dev/null ls -G / | tr -d '\r' | cat)"
echo "$var"

Most commands that do print out those color codes explicitly check if stdout/stderr is a tty (using the isatty function).
If you want to preserve the color codes, you can run it within a terminal emulator like screen or the direct logger script, saving the output to a file.

Related

Diffrence between bash script.sh and ./script.sh [duplicate]

This question already has answers here:
History command works in a terminal, but doesn't when written as a bash script
(3 answers)
Closed 2 years ago.
Suppose we have env.sh file that contains:
echo $(history | tail -n2 | head -n1) | sed 's/[0-9]* //' #looking for the last typed command
when executing this script with bash env.sh, the output will be empty:
but when we execute the script with ./env.sh, we get the last typed command:
I just want to know the diffrence between them
Notice that if we add #!/bin/bash at the beginning of the script, the ./env.sh will no longer output anything.
History is disabled by BASH in non-interactive shells by-default. If you want to enable it however, you can do so like this:
#!/bin/bash
echo $HISTFILE # will be empty in non-iteractive shell
HISTFILE=~/.bash_history # set it again
set -o history
# the command will work now
history
The reason this is done is to avoid cluttering the history by any commands being run by any shell scripts.
Adding hashbang (meaning the file is to be interpreted as a script by the program specified in your hashbang) to your script when being run via ./env.sh invokes your script using the binary /bin/bash i.e. run via bash, thus again printing no history.

Assign output to variable for command run under different user on OSX

I run a bash python command as current user, by doing this:
su $USER -c 'python3 -m site --user-site'
This works properly and prints the following:
/Users/standarduser7/Library/Python/3.6/lib/python/site-packages
I want to assign this output to a variable, so I'm using "$(command)":
target="$(su $USER -c 'python3 -m site --user-site')"
At this point, the OSX terminal hangs and has to be killed. Using backticks instead of "$(command)" leads to same result.
However, if I run the command without user, everything works as it should:
target="$(python3 -m site --user-site)"
echo target
output: /Users/standarduser7/Library/Python/3.6/lib/python/site-packages
How can I assign the output from a command run as the current user to a variable?
I don’t think it’s hanging; I think it’s showing a blank (prompt-less) command-line and is waiting for input. When I key in the user password, it returns this result:
Password:/Users/CK/Library/Python/2.7/lib/python/site-packages
and this is what ends up being stored in the target variable. A quick parameter substitution can rectify this anomalous output:
target=${target#*:}
The other solution (credit given to this answer) is to create a file descriptor as a copy of stdout, then tee the command to the copy, which then allows stdout to be piped to grep in order to process the output:
exec 3>&1 # create a copy of stdout
target=$(su $USER -c "python -m site --user-site" | tee /dev/fd/3 | grep -o '\/.*')
exec 3>&- # close copy

Vim: Warning: Input is not from a terminal

I wrote simple jsonview script to view json files:
#!/bin/bash
tmp_file=/tmp/jsonview.json
cat "${#}" | python -m json.tool > $tmp_file
[[ -f $tmp_file ]] && vim $tmp_file
I am not using less because I need syntax highlighting.
That useless use of cat cat ${#} | ... is so that script can be used as a filter:
jsonview t.json
and:
cat t.json | jsonview
If jsonview used as in second, pipe case - despite the fact that vim is invoked not on pipe but on concrete file, I am getting that warning in subject. I can view json file, but after exit, it messes up terminal. Why is this warning? Why vim thinks that it reads from a pipe?
Vim doesn't like it when standard input is redirected unless you invoke it as vim -. In that case it knows stdin is redirected and handles it. As a side benefit it also lets you get rid of the temp file.
#!/bin/bash
cat "$#" | python -m json.tool | vim +'set syntax=javascript' -R -
Always quote "$#" to ensure file names with whitespace won't mess your script up.
-R gets rid of the prompt to save the buffer when exiting Vim.
you can also do vim $tmp_file </dev/tty to tell vim input is from the terminal and not from standard input.

Pretend to be a tty in bash for any command [duplicate]

This question already has answers here:
How to trick an application into thinking its stdout is a terminal, not a pipe
(9 answers)
Closed 5 years ago.
Whenever I use grep, and I pipe it to an other program, the --color option is not respected. I know I could use --color=always, but It also comes up with some other commands that I would like to get the exact output of that command as the output I would get if I was in a tty.
So my question is, is it possible to trick a command into thinking that the command is run inside a tty ?
For example, running
grep --color word file # Outputs some colors
grep --color word file | cat # Doesn't output any colors
I'd like to be able to write something like :
IS_TTY=TRUE grep --color word file | cat # Outputs some colors
This question seems to have a tool that might do what I want :empty - run processes and applications under pseudo-terminal (PTY), but from what I could read in the docs, I'm not sure it can help for my problem
There are a number of options, as outlined by several other Stack Overflow answers (see Caarlos's comment). I'll summarize them here though:
Use script + printf, requires no extra dependencies:
0<&- script -qefc "ls --color=auto" /dev/null | cat
Or make a bash function faketty to encapsulate it:
faketty () {
script -qefc "$(printf "%q " "$#")" /dev/null
}
faketty ls --color=auto | cat
Or in the fish shell:
function faketty
script -qefc "(printf "%q " "$argv")" /dev/null
end
faketty ls --color=auto | cat
(credit goes to this answer)
http://linux.die.net/man/1/script
Use the unbuffer command (as part of the expect suite of commands), unfortunately this requires an extra package install, but it's the easiest solution:
sudo apt-get install expect-dev # or brew install expect
unbuffer -p ls --color=auto | cat
Or if you use the fish shell:
function faketty
unbuffer -p $argv
end
faketty ls --color=auto | cat
http://linux.die.net/man/1/unbuffer
This is a great article on how TTYs work and what Pseudo-TTYs (PTYs) are, it's worth taking a look at if you want to understand how the linux shell works with file descriptors to pass around input, output, and signals. http://www.linusakesson.net/programming/tty/index.php

Alternatives to BASH/SHELL or ZSHRC

I want an advanced shell or command line in Unix which has the following features:
output to err and out are in different colours.
I should be able to highlight (or find) keywords in the output of the executing command.
indicator in the OS task-bar/title as a command is running or completed.
I am looking at an advanced shell that boosts productivity. Is there any alternative?
Re: output to err and out are in different colours ... can be done in Bash.
# colourize stderr in current shell
# note: use sed in line-buffering mode
(
exec 2> >(sed -l -e $'s/.*/\033[31m&\033[m/')
ls -ld / xxxxx
)
# colourize stderr & stdout in current shell
(
exec 1> >(sed -l -e $'s/.*/\033[32m&\033[m/') 2> >(sed -l -e $'s/.*/\033[31m&\033[m/')
ls -ld / xxxxx
)
That isn't a trivial proposition.
There are shells that work with the terminal to echo the currently executing command in the title bar, such as bash on MacOS X.
The commands are autonomous and do not, in general, colour-code their output. So, to get colour-coded output, the shell will have to capture the error outputs of the commands it runs, and arrange to display that information appropriately colour-coded.
Searching the output requires the terminal program to keep the output it displays in a searchable form, and some program (probably the terminal program or possibly the shell) will have to respond to searching operations.
Emacs allows you to run interactive shells such as bash or zsh.
rc works similarly when run in Plan9 -- I'm not sure about its Unix ports.

Resources