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

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

Related

How to change value of file from a fish function when sudo is required? [duplicate]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 1 year ago.
The community reviewed whether to reopen this question 3 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I've been given sudo access on one of our development RedHat linux boxes, and I seem to find myself quite often needing to redirect output to a location I don't normally have write access to.
The trouble is, this contrived example doesn't work:
sudo ls -hal /root/ > /root/test.out
I just receive the response:
-bash: /root/test.out: Permission denied
How can I get this to work?
Your command does not work because the redirection is performed by your shell which does not have the permission to write to /root/test.out. The redirection of the output is not performed by sudo.
There are multiple solutions:
Run a shell with sudo and give the command to it by using the -c option:
sudo sh -c 'ls -hal /root/ > /root/test.out'
Create a script with your commands and run that script with sudo:
#!/bin/sh
ls -hal /root/ > /root/test.out
Run sudo ls.sh. See Steve Bennett's answer if you don't want to create a temporary file.
Launch a shell with sudo -s then run your commands:
[nobody#so]$ sudo -s
[root#so]# ls -hal /root/ > /root/test.out
[root#so]# ^D
[nobody#so]$
Use sudo tee (if you have to escape a lot when using the -c option):
sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null
The redirect to /dev/null is needed to stop tee from outputting to the screen. To append instead of overwriting the output file
(>>), use tee -a or tee --append (the last one is specific to GNU coreutils).
Thanks go to Jd, Adam J. Forster and Johnathan for the second, third and fourth solutions.
Someone here has just suggested sudoing tee:
sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null
This could also be used to redirect any command, to a directory that you do not have access to. It works because the tee program is effectively an "echo to a file" program, and the redirect to /dev/null is to stop it also outputting to the screen to keep it the same as the original contrived example above.
A trick I figured out myself was
sudo ls -hal /root/ | sudo dd of=/root/test.out
The problem is that the command gets run under sudo, but the redirection gets run under your user. This is done by the shell and there is very little you can do about it.
sudo command > /some/file.log
`-----v-----'`-------v-------'
command redirection
The usual ways of bypassing this are:
Wrap the commands in a script which you call under sudo.
If the commands and/or log file changes, you can make the
script take these as arguments. For example:
sudo log_script command /log/file.txt
Call a shell and pass the command line as a parameter with -c
This is especially useful for one off compound commands.
For example:
sudo bash -c "{ command1 arg; command2 arg; } > /log/file.txt"
Arrange a pipe/subshell with required rights (i.e. sudo)
# Read and append to a file
cat ./'file1.txt' | sudo tee -a '/log/file.txt' > '/dev/null';
# Store both stdout and stderr streams in a file
{ command1 arg; command2 arg; } |& sudo tee -a '/log/file.txt' > '/dev/null';
Yet another variation on the theme:
sudo bash <<EOF
ls -hal /root/ > /root/test.out
EOF
Or of course:
echo 'ls -hal /root/ > /root/test.out' | sudo bash
They have the (tiny) advantage that you don't need to remember any arguments to sudo or sh/bash
Clarifying a bit on why the tee option is preferable
Assuming you have appropriate permission to execute the command that creates the output, if you pipe the output of your command to tee, you only need to elevate tee's privledges with sudo and direct tee to write (or append) to the file in question.
in the example given in the question that would mean:
ls -hal /root/ | sudo tee /root/test.out
for a couple more practical examples:
# kill off one source of annoying advertisements
echo 127.0.0.1 ad.doubleclick.net | sudo tee -a /etc/hosts
# configure eth4 to come up on boot, set IP and netmask (centos 6.4)
echo -e "ONBOOT=\"YES\"\nIPADDR=10.42.84.168\nPREFIX=24" | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth4
In each of these examples you are taking the output of a non-privileged command and writing to a file that is usually only writable by root, which is the origin of your question.
It is a good idea to do it this way because the command that generates the output is not executed with elevated privileges. It doesn't seem to matter here with echo but when the source command is a script that you don't completely trust, it is crucial.
Note you can use the -a option to tee to append append (like >>) to the target file rather than overwrite it (like >).
Make sudo run a shell, like this:
sudo sh -c "echo foo > ~root/out"
The way I would go about this issue is:
If you need to write/replace the file:
echo "some text" | sudo tee /path/to/file
If you need to append to the file:
echo "some text" | sudo tee -a /path/to/file
Don't mean to beat a dead horse, but there are too many answers here that use tee, which means you have to redirect stdout to /dev/null unless you want to see a copy on the screen.
A simpler solution is to just use cat like this:
sudo ls -hal /root/ | sudo bash -c "cat > /root/test.out"
Notice how the redirection is put inside quotes so that it is evaluated by a shell started by sudo instead of the one running it.
How about writing a script?
Filename: myscript
#!/bin/sh
/bin/ls -lah /root > /root/test.out
# end script
Then use sudo to run the script:
sudo ./myscript
Whenever I have to do something like this I just become root:
# sudo -s
# ls -hal /root/ > /root/test.out
# exit
It's probably not the best way, but it works.
I would do it this way:
sudo su -c 'ls -hal /root/ > /root/test.out'
This is based on the answer involving tee. To make things easier I wrote a small script (I call it suwrite) and put it in /usr/local/bin/ with +x permission:
#! /bin/sh
if [ $# = 0 ] ; then
echo "USAGE: <command writing to stdout> | suwrite [-a] <output file 1> ..." >&2
exit 1
fi
for arg in "$#" ; do
if [ ${arg#/dev/} != ${arg} ] ; then
echo "Found dangerous argument ‘$arg’. Will exit."
exit 2
fi
done
sudo tee "$#" > /dev/null
As shown in the USAGE in the code, all you have to do is to pipe the output to this script followed by the desired superuser-accessible filename and it will automatically prompt you for your password if needed (since it includes sudo).
echo test | suwrite /root/test.txt
Note that since this is a simple wrapper for tee, it will also accept tee's -a option to append, and also supports writing to multiple files at the same time.
echo test2 | suwrite -a /root/test.txt
echo test-multi | suwrite /root/test-a.txt /root/test-b.txt
It also has some simplistic protection against writing to /dev/ devices which was a concern mentioned in one of the comments on this page.
sudo at now
at> echo test > /tmp/test.out
at> <EOT>
job 1 at Thu Sep 21 10:49:00 2017
Maybe you been given sudo access to only some programs/paths? Then there is no way to do what you want. (unless you will hack it somehow)
If it is not the case then maybe you can write bash script:
cat > myscript.sh
#!/bin/sh
ls -hal /root/ > /root/test.out
Press ctrl + d :
chmod a+x myscript.sh
sudo myscript.sh
Hope it help.

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.

How to embed and run a multi-line perl script stored in a bash variable in a bash script (without immediately running perl) [duplicate]

This question already has answers here:
Run Perl Script From Unix Shell Script
(2 answers)
Closed 3 years ago.
How do I replace [RUN_ABOVE_PERL_SORTING_SCRIPT_HERE] with something that runs this perl script stored in a bash variable?
#!/usr/bin/env bash
# The perl script to sort getfacl output:
# https://github.com/philips/acl/blob/master/test/sort-getfacl-output
find /etc -name .git -prune -o -print | xargs getfacl -peL | [RUN_ABOVE_PERL_SORTING_SCRIPT_HERE] > /etc/.facl.nogit.txt
Notes:
I do not want to employ 2 files (a bash script and a perl script) to solve this problem; I want the functionality to be stored all in one bash script file.
I do not want to immediately run the perl script when storing the perl-script variable, because I want to run it later in the getfacl(1) bash pipeline shown below.
There's many similar stackoverflow questions+answers, but none that I can find (that has clean-reading code, anyway?) that solve both the a) multi-line and b) delayed-execution (or the embedded perl script) portion of this problem.
And to clarify: this problem is not specifically about getfacl(1), which is simply an catalyst to explore how to embed perl scripts--and possibly other scripting languages like python--into bash variables for delayed execution in a bash script.)
Employ the bash read command, which reads the perl script into a variable that's executed later in the bash script.
#!/usr/bin/env bash
# sort getfacl output: the following code is copied from:
# https://github.com/philips/acl/blob/master/test/sort-getfacl-output
read -r -d '' SCRIPT <<'EOS'
#!/usr/bin/env perl -w
undef $/;
print join("\n\n", sort split(/\n\n/, <>)), "\n\n";
EOS
find /etc -name .git -prune -o -print | xargs getfacl -peL | perl -e "$SCRIPT" > /etc/.facl.nogit.txt
This is covered by Run Perl Script From Unix Shell Script.
As they apply here:
You can pass the code to Perl using -e/-E.
perl -e"$script"
or
perl -e"$( curl "$url" )"
You can pass the code via STDIN.
printf %s "$script" | perl -e"$script"
or
curl "$url" | perl
(This won't work for you because you need STDIN.)
You can create a virtual file.
perl <( printf %s "$script" )
or
perl <( curl "$url" )
You can take advantage of perl's -x option.
(Not applicable if you want to download the script dynamically.)
All of the above assume the following command has already been executed:
url='https://github.com/philips/acl/blob/master/test/sort-getfacl-output'
Some of the above assume the following command has already been executed:
script="$( curl "$url" )

Apply sudo on whole command line (stdout redirection) [duplicate]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 1 year ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I've been given sudo access on one of our development RedHat linux boxes, and I seem to find myself quite often needing to redirect output to a location I don't normally have write access to.
The trouble is, this contrived example doesn't work:
sudo ls -hal /root/ > /root/test.out
I just receive the response:
-bash: /root/test.out: Permission denied
How can I get this to work?
Your command does not work because the redirection is performed by your shell which does not have the permission to write to /root/test.out. The redirection of the output is not performed by sudo.
There are multiple solutions:
Run a shell with sudo and give the command to it by using the -c option:
sudo sh -c 'ls -hal /root/ > /root/test.out'
Create a script with your commands and run that script with sudo:
#!/bin/sh
ls -hal /root/ > /root/test.out
Run sudo ls.sh. See Steve Bennett's answer if you don't want to create a temporary file.
Launch a shell with sudo -s then run your commands:
[nobody#so]$ sudo -s
[root#so]# ls -hal /root/ > /root/test.out
[root#so]# ^D
[nobody#so]$
Use sudo tee (if you have to escape a lot when using the -c option):
sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null
The redirect to /dev/null is needed to stop tee from outputting to the screen. To append instead of overwriting the output file
(>>), use tee -a or tee --append (the last one is specific to GNU coreutils).
Thanks go to Jd, Adam J. Forster and Johnathan for the second, third and fourth solutions.
Someone here has just suggested sudoing tee:
sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null
This could also be used to redirect any command, to a directory that you do not have access to. It works because the tee program is effectively an "echo to a file" program, and the redirect to /dev/null is to stop it also outputting to the screen to keep it the same as the original contrived example above.
A trick I figured out myself was
sudo ls -hal /root/ | sudo dd of=/root/test.out
The problem is that the command gets run under sudo, but the redirection gets run under your user. This is done by the shell and there is very little you can do about it.
sudo command > /some/file.log
`-----v-----'`-------v-------'
command redirection
The usual ways of bypassing this are:
Wrap the commands in a script which you call under sudo.
If the commands and/or log file changes, you can make the
script take these as arguments. For example:
sudo log_script command /log/file.txt
Call a shell and pass the command line as a parameter with -c
This is especially useful for one off compound commands.
For example:
sudo bash -c "{ command1 arg; command2 arg; } > /log/file.txt"
Arrange a pipe/subshell with required rights (i.e. sudo)
# Read and append to a file
cat ./'file1.txt' | sudo tee -a '/log/file.txt' > '/dev/null';
# Store both stdout and stderr streams in a file
{ command1 arg; command2 arg; } |& sudo tee -a '/log/file.txt' > '/dev/null';
Yet another variation on the theme:
sudo bash <<EOF
ls -hal /root/ > /root/test.out
EOF
Or of course:
echo 'ls -hal /root/ > /root/test.out' | sudo bash
They have the (tiny) advantage that you don't need to remember any arguments to sudo or sh/bash
Clarifying a bit on why the tee option is preferable
Assuming you have appropriate permission to execute the command that creates the output, if you pipe the output of your command to tee, you only need to elevate tee's privledges with sudo and direct tee to write (or append) to the file in question.
in the example given in the question that would mean:
ls -hal /root/ | sudo tee /root/test.out
for a couple more practical examples:
# kill off one source of annoying advertisements
echo 127.0.0.1 ad.doubleclick.net | sudo tee -a /etc/hosts
# configure eth4 to come up on boot, set IP and netmask (centos 6.4)
echo -e "ONBOOT=\"YES\"\nIPADDR=10.42.84.168\nPREFIX=24" | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth4
In each of these examples you are taking the output of a non-privileged command and writing to a file that is usually only writable by root, which is the origin of your question.
It is a good idea to do it this way because the command that generates the output is not executed with elevated privileges. It doesn't seem to matter here with echo but when the source command is a script that you don't completely trust, it is crucial.
Note you can use the -a option to tee to append append (like >>) to the target file rather than overwrite it (like >).
Make sudo run a shell, like this:
sudo sh -c "echo foo > ~root/out"
The way I would go about this issue is:
If you need to write/replace the file:
echo "some text" | sudo tee /path/to/file
If you need to append to the file:
echo "some text" | sudo tee -a /path/to/file
Don't mean to beat a dead horse, but there are too many answers here that use tee, which means you have to redirect stdout to /dev/null unless you want to see a copy on the screen.
A simpler solution is to just use cat like this:
sudo ls -hal /root/ | sudo bash -c "cat > /root/test.out"
Notice how the redirection is put inside quotes so that it is evaluated by a shell started by sudo instead of the one running it.
How about writing a script?
Filename: myscript
#!/bin/sh
/bin/ls -lah /root > /root/test.out
# end script
Then use sudo to run the script:
sudo ./myscript
Whenever I have to do something like this I just become root:
# sudo -s
# ls -hal /root/ > /root/test.out
# exit
It's probably not the best way, but it works.
I would do it this way:
sudo su -c 'ls -hal /root/ > /root/test.out'
This is based on the answer involving tee. To make things easier I wrote a small script (I call it suwrite) and put it in /usr/local/bin/ with +x permission:
#! /bin/sh
if [ $# = 0 ] ; then
echo "USAGE: <command writing to stdout> | suwrite [-a] <output file 1> ..." >&2
exit 1
fi
for arg in "$#" ; do
if [ ${arg#/dev/} != ${arg} ] ; then
echo "Found dangerous argument ‘$arg’. Will exit."
exit 2
fi
done
sudo tee "$#" > /dev/null
As shown in the USAGE in the code, all you have to do is to pipe the output to this script followed by the desired superuser-accessible filename and it will automatically prompt you for your password if needed (since it includes sudo).
echo test | suwrite /root/test.txt
Note that since this is a simple wrapper for tee, it will also accept tee's -a option to append, and also supports writing to multiple files at the same time.
echo test2 | suwrite -a /root/test.txt
echo test-multi | suwrite /root/test-a.txt /root/test-b.txt
It also has some simplistic protection against writing to /dev/ devices which was a concern mentioned in one of the comments on this page.
sudo at now
at> echo test > /tmp/test.out
at> <EOT>
job 1 at Thu Sep 21 10:49:00 2017
Maybe you been given sudo access to only some programs/paths? Then there is no way to do what you want. (unless you will hack it somehow)
If it is not the case then maybe you can write bash script:
cat > myscript.sh
#!/bin/sh
ls -hal /root/ > /root/test.out
Press ctrl + d :
chmod a+x myscript.sh
sudo myscript.sh
Hope it help.

bash command preserve color when piping [duplicate]

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.

Resources