Reputation: 150
I'm trying to write a function that I call logAction
specifically for Ksh
that reads input from a pipe and accepts arguments in the same function call. I've been trying to debug this for 2 weeks searching on Google and sites like this.
The purpose of the function that I'm trying to create is to simplify the process of logging output to a log file for other scripts that I've developed. I intend to use the command for a variety of situations, but the 2 easiest to describe and illustrate what I'm attempting are similar to the following:
Syntax Example 1:
logAction 1 "Script executed some action."
Syntax Example 2:
COMMAND1 | COMMAND2 | logAction 1
As you can see, I want to be able to pass the intended log information to the function either as an argument or as stdinput through a pipe in order to capture the output of a command sequence. A 3rd scenario would be to prepend some text before the piped output, i.e.
Syntax Example 3:
COMMAND1 | COMMAND2 | logAction 1 "This is the output: "
The output below illustrates what the result would be from using the syntax in Example 3:
[2015/12/28 10:20:32] This is the output: <Output from COMMAND1 | COMMAND 2>
Here's a basic skeleton of the function I'm trying to create.
function logAction {
while read -t 0 -u 0 stdinput
do
echo "StdInput:'$stdinput'"
pipedinput=$stdinput
done
pipedinput=${pipedinput:-}
case $1 in
1) #Output text only to log file
output=$(echo "$pretag ${@:2} $pipedinput")
echo $output >> $LOG
2) #Output text to screen and log file
output=$(echo "$pretag ${@:2} $pipedinput")
echo $output
echo $output >> $LOG
3)
....
esac
unset pipedinput
}
Originally, I didn't supply the -t
argument to the read
command, and the function would pause execution until the user entered CTRL-D
to indicate EOF
. However, for my purposes, this function is intended to have silent operation and should not request user input. Adding the -t 0
argument with the read
command tells the read command to timeout with 0 seconds instead of waiting for EOF
from the keyboard.
This solved the problem for some cases; however, the read
command doesn't capture any input when used with the cat
command like so:
cat somefile.txt | logAction 1
cat somefile.txt | awk '{ a=a" "$1 }END{ print a }' | logAction 1
Yet using echo
seems to work:
echo "ab bc cd de ef fg gh" | logAction 1 "This is the output: "
I have been able to make it work with cat
by changing the 0 for the timeout to 0.1 (i.e. read -t 0.1
), but I'm concerned that this is a dirty solution. This seems to indicate there is a race condition where the read
command is being executed before the pipe has finished its output and closed. This wasn't what I expected, as I thought the preceding command in a pipe sequence must finish execution before the subsequent command is executed.
Is there a way to tell the read
command to ignore stdin from the keyboard and use the stdout from the preceeding command in the pipe? I tried to tell it to use FD0
(which I thought was the stdout from the pipe, but it seems to also be connected to the keyboard) with the the -u 0
argument, but that didn't seem to work.
Upvotes: 1
Views: 3483
Reputation: 8943
Try this:
#!/bin/ksh
function log
{
ARG1=$1
ARG2=$2
if [ ! -t 0 ]
then
INPUT=$(cat)
else
INPUT=""
fi
echo "<datetime>" ${ARG2}${INPUT}
}
log 1 "Script executed some action."
echo "<data from pipe>" | log 2 "this is the output: "
echo "<data from pipe>" | cat | log 2 "this is the output: "
Output:
<datetime> Script executed some action.
<datetime> this is the output: <data from pipe>
<datetime> this is the output: <data from pipe>
Upvotes: 5