Home > Enterprise >  Is there a way to extract the first line from stderr and store it to a variable in the shell script
Is there a way to extract the first line from stderr and store it to a variable in the shell script

Time:11-20

Say I have a command I'm running in my script who's first line in stderr is something I need. I'm using stderr because stdout is already being used for transferring some other data. I still need the rest of stderr for user feedback, so I still want to display everything after the first line.

cmd() {
  ssh [email protected] '
    printf "stderr data line 1 (important)\n" 1>&2
    printf "stdout data line 1\n"
    printf "stderr data line 2\n" 1>&2
    printf "stdout data line 2\n"
    printf "stdout data line 3\n"
    printf "stderr data line 3\n" 1>&2'
}

# What sort of shell magic would I need to extract
# only the 1st line of stderr?
cmd > store_stdout_to_this_file ??? read -a first_line_of_stderr
echo "$first_line_of_stderr"

I can't use a pipe, as pipes only pipe stdout, and even if I were to rearrange them, then the other end of the pipe is in a different process space.

CodePudding user response:

I see several methods to do this, all with different limitations and oddities. Probably the simplest is to redirect stderr to a background subshell (via process substitution) that runs read once, then cat:

cmd >outputfile 2> >(read firstline; echo "First line is: '$firstline'"; cat -u)

But that puts the first-line processing in a subshell, so any variables it sets will be lost when that shell exits. I suppose you could have it write the line to another file, and then read that from the main script afterward...

Another possibility is to put the command you're trying to capture in the process substitution, and read and cat in the main shell:

{ read firstline; cat -u; } < <(cmd 2>&1 >outputfile)
echo "First line is: '$firstline'"

Note that the output redirection from cmd must be done inside the process-substituted part (since its stdout is being sent to the read-cat part), and the 2>&1 must be first so stderr doesn't go to the same place.

CodePudding user response:

You can do like this:

line=
while read -r; do
   [[ -z $line ]] && line="$REPLY" || echo "$REPLY"
done < <(cmd 2>&1 >out.log)

stderr data line 2
stderr data line 3

# check $line
echo "$line"

stderr data line 1 (important)

i.e. redirect stderr to stdout first and then redirect stdout to out.log and finally pipe output using head -n 1.

  • Related