Newsgroups: comp.unix.shell
> Is there a way that I control whether piping or not by a variable in > sh script?
I thought about this problem, too, and as far as I can see there is no really elegant solution.
What I came up with now uses Bash's process substitution:
if [ ...some conditions... ] then exec 3> >( tee log ) # Here goes your pipe! else exec 3>&1 # To stdout otherwise. fi
{ date; # The producer: Do your stuff here. } >&3
exec 3>- # Close file descriptor (and end pipe).
Martin Ramsch
Yeah, Martin, you hit right on the nail top. Seems to be an elegant solution to me. :-) I'll play more with it. Thanks
bash$ exec 3> >( tee log )
bash$ date>&3 Mon May 29 10:06:19 ADT 2000
bash$ exec 3>&-
bash$ cat log Mon May 29 10:06:19 ADT 2000
exec > >( tee log | awk '{print $1}' ) ; ls -l ; exec >&-
sh$ dev_dest=/dev/null sh$ echo aaa > $dev_dest sh$ dev_dest=/dev/tty sh$ echo aaa > $dev_dest aaa sh$
> > What I came up with now uses Bash's process substitution: > > > > exec 3> >( tee log ) # Here goes your pipe! > > ... > > exec 3>&- # Close file descriptor (and end pipe). > > is there way to do the above bash trick in sh script? thanks
In plain sh the effect of process substitution can be done with explicitly created named pipes:
if [ ...some conditions... ] then NPIPE=/tmp/pipe.$$ # filename for auxiliary named pipe. mknod $NPIPE p # create named pipe
# Now start into the backgroud all commands # which are to read from the named pipe: { sort | tee log.txt; echo "Done."; } <$NPIPE &
exec 3>$NPIPE # file descriptor 3 writes into the named pipe else exec 3>&1 # in this case file desc 3 simply _is_ STDOUT fi
{ date; ... # Do your stuff here. } >&3 # Every output is written to file desc #3.
exec 3>&- # Close file desc #3. # This causes EOF for the background commands. if [ "$NPIPE" != "" ] then rm $NPIPE # Remove auxiliary named pipe again. unset NPIPE fi
Martin
Since no particular problem was mentioned let me try to create one.
PROBLEM: Create a command which does the following: if $1 is '/' pipe date through tr and convert all : to / else just echo date.
The "straight forward" way of doing this is to use a script like this
if [[ $1 = "/" ]] then date | tr : / else date fi
There are many alternatives
Use a variable for the command.
CMD="cat" [[ $1 = "/" ]] && CMD="tr : / " date | ${CMD}
will do the job.
Or a simpler version of this (without the cat) would be
CMD="" [[ $1 = "/" ]] && CMD="| tr : /" eval date $CMD
or a clearer version would be to just say
CMD="date" [[ $1 = "/" ]] && CMD="date | tr : /" eval $CMD
All the above used variables that have been set up prior to the command so the conditional is not checked in the actual statement.
Try these if you want to do the checking of the conditional in the pipe
date | ( ( [[ $1 = "/" ]] && tr : / ) || cat ) # please ensure there is a space between two ((s
or more explicitly as
date | if [[ $1 = "/" ]] then tr : / else cat fi
if you dont like redundant cats ( not many do ) then try this
eval date $( [[ $1 = "/" ]] && print "| tr : /" )
It is interesting to see how much you can play around to accomplish the same thing. I am sure that the gurus here can think of more ways.
Play around :-)
Raja
$ a=
$ date | ( ( [[ $a = "/" ]] && tr : / ) || cat ) Fri Mar 22 13:18:44 CST 2002
$ a=/
$ date | ( ( [[ $a = "/" ]] && tr : / ) || cat ) Fri Mar 22 13/20/02 CST 2002
> I tried to extend this solution for my "simple" task, which involes more > than one piping, but failed. So the above is good for one comand piping.(?)
> CMD="cat | tr : / " # test a more than one piping case > > and all the following failed: > > date | ${CMD} > eval date | ${CMD} > date | ( ${CMD} )
Try this as an example of a multiple pipeline command:
[[ $1 = / ]] && CMD="| tr : / | wc -l " eval date $CMD
would succeed. This illustrates two things:
If the pipeline contains nested quotes or "special" characters like (; | {}() $ []) etc then it is better to make a function out of the whole complex pipeline and call that function conditionally.
For instance in the above illustration we can rewrite it as :
function f1 { tr : / | wc -l }
[[ $1 = / ]] && CMD="| f1" eval date $CMD
multiple pipelines can also be handled by the other versions of the script that I had mentioned.
for instance it is possible to write:
eval date $( [[ $1 = / ]] && print "| tr : / | wc -l" )
Raja
>and all the following failed: > >date | ${CMD} >eval date | ${CMD} >date | ( ${CMD} )
That's because you want either:
date | eval ${CMD}
or:
eval date \| ${CMD}
The $CMD needs to be eval'd in order to get its internal pipeline interpreted correctly, and the one example you showed that has an eval statement is just doing an "eval date" and piping that to $CMD, which isn't right (though you could, if you were feeling silly:).
eval date | eval ${CMD}
Ken Pizzini