Standard input and standard output are not commands.
Imagine commands as machines in a factory with an assembly line. Most machines are designed to have one conveyor belt to feed data in and one conveyor belt to feed data out; they are the standard input and the standard output respectively. The standard error is an opening on the side of the machine where it can eject rejects.
+-------+ +------------------+ +------------------+ +------+
| input | | machine A | | machine B | |output|
| reser |=====|<stdin stdout>|=======|<stdin stdout>|=====|bucket|
| ‑voir | → | stderr | → | stderr | → | |
+-------+ +------------------+ +------------------+ +------+
|| ||
The diagram above shows a conveyor belt that goes through two machines. The data comes from the input reservoir on the left, is fed to machine A, then the output is conveyed further to machine B (for which it is input), and machine B's output is deposited in the output bucket on the right.
In unix terms, this is called a pipeline. The metaphor is that of plumbing: a pipe connects machine A to machine B. The shell syntax for the pipeline above is
<input-file.txt commandA | commandB >output-file.txt
The <
redirection symbol tells the shell to connect commandA
's standard input to the file input-file.txt
before launching commandA
. (You can put the redirection before or after the command name.) The >
redirection symbol tells the shell to connect commandB
's standard output to output-file.txt
. The pipe ("|
") symbol in the middle tells the shell to connect commandA
's standard output to commandB
's standard input before launching them.
Commands can have more than one input and more than one output, but that's material for another day.
Revisiting an old question to provide a less effort implementation. In fact, two questions in one:
a) Handling STDERR
in a select
command
b) Change the appearance of items shown in the select
command
Alternate approaches have been addressed already -- i.e. meaning solutions that don't use the select
command. So I will not walk this path.
The select
command is quite handy in that it automatically handles the column layout of your options, and essentially all you have to do is the case handling. So rather then sort of reinventing the wheel it can be a good solution for many simple cases.
a) Handling of STDERR
You have already the solution in the original question: redirect STDERR
to STDOUT
.
select choice in ...; do
...
done 2>&1
Both file handles offer the same capabilities, so there is no reason, IMHO, to fiddle around with this. Using STDERR
for select
is actually a good idea as you do not clobber your STDOUT
and can use it for other purposes, such as, for instance, redirection.
b) Change the appearance of items shown in the select
command
The solution to enhance the output is pretty trivial here too if you adopt a syntax in the likes of:
select choice in $(...); do
...
done
Use $(...)
to perform whatever make up of the select items
.
Your could for instance, on an ANSI terminal, colourise them:
$(for i in *; do print "\[E31m$i\E[0m"; done)
or call a colourisation function (which becomes reusable in other circumstances):
function colourise_diritems {
typeset dir=$1 item
for item in $dir/*; do
if [[ -d $item ]]; then print "\E[31m$i\E[0m"
elif [[ -f $item ]]; then print "\E[32m$item\E[0m"
...
fi
done
}
select choice in $(colourise_diritems $PWD); do
...
done
Obviously if you add markup to the items, you'll have to clean up the result before using it; in the above example, before using the selected choice
, you would have to do something like
choice=${choice#\E[+([^m]*)m}
choice=${choice%\E[+([^m]*)m}
to remove the ANSI sequences.
Note: playing around with ANSI sequences can be tricky and requires further testing.
Best Answer
First, you should avoid
echo
to output arbitrary data.On systems other than Linux-based ones, you could use:
For Linux, that works for some types of stdout, but that fails when stdout is a socket or worse, if stdout is a regular file, that would truncate that file instead of writing at the current position stdout is in the file.
Other than that, in Bourne-like shell, there's no way to have conditional redirection, though you could use
eval
:Instead of a variable, you could use a dedicated file descriptor:
A (small) downside with that is that except in
ksh
, that fd 3 would be leaked to every command run in the script. Withzsh
, you can dosysopen -wu 3 -o cloexec -- "$logfile" || exit
in place ofexec 3> "$logfile"
butbash
has no equivalent.Another common idiom is to use a function like: