Redirect stderr to a bash variable and the stdout to another bash variable without using temporary files

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
2
down vote

favorite
1












func() 
echo 'hello'
echo 'This is an error' >&2


a=$(func)
b=???


I'd like to redirect the stderr to b variable without creating a temporary file.



 echo $b
# output should be: "This is an error"


The solution that works but with a temporary file:



touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt


So the question is, how do I redirect the stderr of the bash function func to the variable b without the need of a temporary file?







share|improve this question




















  • Related SO question that may be useful: stackoverflow.com/q/962255/1640661
    – Anthony Geoghegan
    Mar 14 at 12:40










  • @AnthonyGeoghegan I'd like to store both stderr and stdout in two variables, not only the stderr
    – smarber
    Mar 14 at 12:54






  • 1




    From their comments, it seems the SO questioner also wanted to preserve the stdout (pass it on a pipe). I don't think they got a completely satisfactory answer but I thought the link still might be of interest to you.
    – Anthony Geoghegan
    Mar 14 at 13:01















up vote
2
down vote

favorite
1












func() 
echo 'hello'
echo 'This is an error' >&2


a=$(func)
b=???


I'd like to redirect the stderr to b variable without creating a temporary file.



 echo $b
# output should be: "This is an error"


The solution that works but with a temporary file:



touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt


So the question is, how do I redirect the stderr of the bash function func to the variable b without the need of a temporary file?







share|improve this question




















  • Related SO question that may be useful: stackoverflow.com/q/962255/1640661
    – Anthony Geoghegan
    Mar 14 at 12:40










  • @AnthonyGeoghegan I'd like to store both stderr and stdout in two variables, not only the stderr
    – smarber
    Mar 14 at 12:54






  • 1




    From their comments, it seems the SO questioner also wanted to preserve the stdout (pass it on a pipe). I don't think they got a completely satisfactory answer but I thought the link still might be of interest to you.
    – Anthony Geoghegan
    Mar 14 at 13:01













up vote
2
down vote

favorite
1









up vote
2
down vote

favorite
1






1





func() 
echo 'hello'
echo 'This is an error' >&2


a=$(func)
b=???


I'd like to redirect the stderr to b variable without creating a temporary file.



 echo $b
# output should be: "This is an error"


The solution that works but with a temporary file:



touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt


So the question is, how do I redirect the stderr of the bash function func to the variable b without the need of a temporary file?







share|improve this question












func() 
echo 'hello'
echo 'This is an error' >&2


a=$(func)
b=???


I'd like to redirect the stderr to b variable without creating a temporary file.



 echo $b
# output should be: "This is an error"


The solution that works but with a temporary file:



touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt


So the question is, how do I redirect the stderr of the bash function func to the variable b without the need of a temporary file?









share|improve this question











share|improve this question




share|improve this question










asked Mar 14 at 12:09









smarber

3011213




3011213











  • Related SO question that may be useful: stackoverflow.com/q/962255/1640661
    – Anthony Geoghegan
    Mar 14 at 12:40










  • @AnthonyGeoghegan I'd like to store both stderr and stdout in two variables, not only the stderr
    – smarber
    Mar 14 at 12:54






  • 1




    From their comments, it seems the SO questioner also wanted to preserve the stdout (pass it on a pipe). I don't think they got a completely satisfactory answer but I thought the link still might be of interest to you.
    – Anthony Geoghegan
    Mar 14 at 13:01

















  • Related SO question that may be useful: stackoverflow.com/q/962255/1640661
    – Anthony Geoghegan
    Mar 14 at 12:40










  • @AnthonyGeoghegan I'd like to store both stderr and stdout in two variables, not only the stderr
    – smarber
    Mar 14 at 12:54






  • 1




    From their comments, it seems the SO questioner also wanted to preserve the stdout (pass it on a pipe). I don't think they got a completely satisfactory answer but I thought the link still might be of interest to you.
    – Anthony Geoghegan
    Mar 14 at 13:01
















Related SO question that may be useful: stackoverflow.com/q/962255/1640661
– Anthony Geoghegan
Mar 14 at 12:40




Related SO question that may be useful: stackoverflow.com/q/962255/1640661
– Anthony Geoghegan
Mar 14 at 12:40












@AnthonyGeoghegan I'd like to store both stderr and stdout in two variables, not only the stderr
– smarber
Mar 14 at 12:54




@AnthonyGeoghegan I'd like to store both stderr and stdout in two variables, not only the stderr
– smarber
Mar 14 at 12:54




1




1




From their comments, it seems the SO questioner also wanted to preserve the stdout (pass it on a pipe). I don't think they got a completely satisfactory answer but I thought the link still might be of interest to you.
– Anthony Geoghegan
Mar 14 at 13:01





From their comments, it seems the SO questioner also wanted to preserve the stdout (pass it on a pipe). I don't think they got a completely satisfactory answer but I thought the link still might be of interest to you.
– Anthony Geoghegan
Mar 14 at 13:01











2 Answers
2






active

oldest

votes

















up vote
3
down vote













On Linux and with shells that implement here-documents with writable temporary files (like bash does), you can do:




out=$(ls /dev/null /x 2> /dev/fd/3)
err=$(cat<&3)
3<<EOF
EOF

printf '%s=<%s>n' out "$out" err "$err"


(where ls /dev/null /x is an example command that outputs something on both stdout and stderr).



With zsh, you can also do:



() out=$(ls /dev/null /x 2> $1) err=$(<$1); =(:)


(where =(cmd) is a form of process substitution that uses temporary files, and () code; args anonymous functions).



In any case, you'd want to use temporary files. Any solution that would use pipes would be prone to deadlocks in case of large outputs. You could read stdout and stderr through two separate pipes and use select()/poll() and some reads in a loop to read data as it comes from the two pipes without causing lock-ups, but that would be quite involved and AFAIK, only zsh has select() support built-in.



Another approach could be to store one of the streams in temporary memory instead of a temporary file. Like (zsh or bash syntax):




IFS= read -rd '' err
IFS= read -rd '' out
< <( out=$(ls /dev/null /x); 2>&1; printf '%s' "$out")


(assuming the command doesn't output any NUL)



Note that $err will include the trailing newline character.



Other approaches could be to decorate the stdout and stderr differently and remove the decoration upon reading:



out= err=
while IFS= read -r line; do
case $line in
(out:*) out=$out$line#out:$'n';;
(err:*) out=$out$line#err:$'n';;
esac
done < <(
ls /dev/null/x 2>&1 |
grep --label=err --line-buffered -H '^'
)


That assumes GNU grep and that the lines are short enough. With lines bigger than PIPEBUF (4K on Linux), lines of the output of the two greps could end up being mangled together in chunks.






share|improve this answer





























    up vote
    1
    down vote













    Well, capturing the stderr in one variable and stdout in another variable without temporary file is not easy at all.



    Here is an example that works



    func() 
    echo 'hello'
    echo 'This is an error' >&2


    result=$(
    stdout=$(func) ; 2>&1
    echo -e "mysuperuniqueseparatorn"
    echo -e "$stdoutn"
    )
    var_out=$result#*mysuperuniqueseparator$'n'
    var_err=$result%$'n'mysuperuniqueseparator*


    I'm not happy with it because it's a dirty way, redirect the stderr to stdout and put both in one variable with a separator between them and then break it down into two pieces.



    Plus :




    Obviously, this is not robust, because either the standard output or the standard error of the command could contain whatever separator string you employ.




    Taken from here http://mywiki.wooledge.org/BashFAQ/002






    share|improve this answer




















      Your Answer







      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "106"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: false,
      noModals: false,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );








       

      draft saved


      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f430161%2fredirect-stderr-to-a-bash-variable-and-the-stdout-to-another-bash-variable-witho%23new-answer', 'question_page');

      );

      Post as a guest






























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      3
      down vote













      On Linux and with shells that implement here-documents with writable temporary files (like bash does), you can do:




      out=$(ls /dev/null /x 2> /dev/fd/3)
      err=$(cat<&3)
      3<<EOF
      EOF

      printf '%s=<%s>n' out "$out" err "$err"


      (where ls /dev/null /x is an example command that outputs something on both stdout and stderr).



      With zsh, you can also do:



      () out=$(ls /dev/null /x 2> $1) err=$(<$1); =(:)


      (where =(cmd) is a form of process substitution that uses temporary files, and () code; args anonymous functions).



      In any case, you'd want to use temporary files. Any solution that would use pipes would be prone to deadlocks in case of large outputs. You could read stdout and stderr through two separate pipes and use select()/poll() and some reads in a loop to read data as it comes from the two pipes without causing lock-ups, but that would be quite involved and AFAIK, only zsh has select() support built-in.



      Another approach could be to store one of the streams in temporary memory instead of a temporary file. Like (zsh or bash syntax):




      IFS= read -rd '' err
      IFS= read -rd '' out
      < <( out=$(ls /dev/null /x); 2>&1; printf '%s' "$out")


      (assuming the command doesn't output any NUL)



      Note that $err will include the trailing newline character.



      Other approaches could be to decorate the stdout and stderr differently and remove the decoration upon reading:



      out= err=
      while IFS= read -r line; do
      case $line in
      (out:*) out=$out$line#out:$'n';;
      (err:*) out=$out$line#err:$'n';;
      esac
      done < <(
      ls /dev/null/x 2>&1 |
      grep --label=err --line-buffered -H '^'
      )


      That assumes GNU grep and that the lines are short enough. With lines bigger than PIPEBUF (4K on Linux), lines of the output of the two greps could end up being mangled together in chunks.






      share|improve this answer


























        up vote
        3
        down vote













        On Linux and with shells that implement here-documents with writable temporary files (like bash does), you can do:




        out=$(ls /dev/null /x 2> /dev/fd/3)
        err=$(cat<&3)
        3<<EOF
        EOF

        printf '%s=<%s>n' out "$out" err "$err"


        (where ls /dev/null /x is an example command that outputs something on both stdout and stderr).



        With zsh, you can also do:



        () out=$(ls /dev/null /x 2> $1) err=$(<$1); =(:)


        (where =(cmd) is a form of process substitution that uses temporary files, and () code; args anonymous functions).



        In any case, you'd want to use temporary files. Any solution that would use pipes would be prone to deadlocks in case of large outputs. You could read stdout and stderr through two separate pipes and use select()/poll() and some reads in a loop to read data as it comes from the two pipes without causing lock-ups, but that would be quite involved and AFAIK, only zsh has select() support built-in.



        Another approach could be to store one of the streams in temporary memory instead of a temporary file. Like (zsh or bash syntax):




        IFS= read -rd '' err
        IFS= read -rd '' out
        < <( out=$(ls /dev/null /x); 2>&1; printf '%s' "$out")


        (assuming the command doesn't output any NUL)



        Note that $err will include the trailing newline character.



        Other approaches could be to decorate the stdout and stderr differently and remove the decoration upon reading:



        out= err=
        while IFS= read -r line; do
        case $line in
        (out:*) out=$out$line#out:$'n';;
        (err:*) out=$out$line#err:$'n';;
        esac
        done < <(
        ls /dev/null/x 2>&1 |
        grep --label=err --line-buffered -H '^'
        )


        That assumes GNU grep and that the lines are short enough. With lines bigger than PIPEBUF (4K on Linux), lines of the output of the two greps could end up being mangled together in chunks.






        share|improve this answer
























          up vote
          3
          down vote










          up vote
          3
          down vote









          On Linux and with shells that implement here-documents with writable temporary files (like bash does), you can do:




          out=$(ls /dev/null /x 2> /dev/fd/3)
          err=$(cat<&3)
          3<<EOF
          EOF

          printf '%s=<%s>n' out "$out" err "$err"


          (where ls /dev/null /x is an example command that outputs something on both stdout and stderr).



          With zsh, you can also do:



          () out=$(ls /dev/null /x 2> $1) err=$(<$1); =(:)


          (where =(cmd) is a form of process substitution that uses temporary files, and () code; args anonymous functions).



          In any case, you'd want to use temporary files. Any solution that would use pipes would be prone to deadlocks in case of large outputs. You could read stdout and stderr through two separate pipes and use select()/poll() and some reads in a loop to read data as it comes from the two pipes without causing lock-ups, but that would be quite involved and AFAIK, only zsh has select() support built-in.



          Another approach could be to store one of the streams in temporary memory instead of a temporary file. Like (zsh or bash syntax):




          IFS= read -rd '' err
          IFS= read -rd '' out
          < <( out=$(ls /dev/null /x); 2>&1; printf '%s' "$out")


          (assuming the command doesn't output any NUL)



          Note that $err will include the trailing newline character.



          Other approaches could be to decorate the stdout and stderr differently and remove the decoration upon reading:



          out= err=
          while IFS= read -r line; do
          case $line in
          (out:*) out=$out$line#out:$'n';;
          (err:*) out=$out$line#err:$'n';;
          esac
          done < <(
          ls /dev/null/x 2>&1 |
          grep --label=err --line-buffered -H '^'
          )


          That assumes GNU grep and that the lines are short enough. With lines bigger than PIPEBUF (4K on Linux), lines of the output of the two greps could end up being mangled together in chunks.






          share|improve this answer














          On Linux and with shells that implement here-documents with writable temporary files (like bash does), you can do:




          out=$(ls /dev/null /x 2> /dev/fd/3)
          err=$(cat<&3)
          3<<EOF
          EOF

          printf '%s=<%s>n' out "$out" err "$err"


          (where ls /dev/null /x is an example command that outputs something on both stdout and stderr).



          With zsh, you can also do:



          () out=$(ls /dev/null /x 2> $1) err=$(<$1); =(:)


          (where =(cmd) is a form of process substitution that uses temporary files, and () code; args anonymous functions).



          In any case, you'd want to use temporary files. Any solution that would use pipes would be prone to deadlocks in case of large outputs. You could read stdout and stderr through two separate pipes and use select()/poll() and some reads in a loop to read data as it comes from the two pipes without causing lock-ups, but that would be quite involved and AFAIK, only zsh has select() support built-in.



          Another approach could be to store one of the streams in temporary memory instead of a temporary file. Like (zsh or bash syntax):




          IFS= read -rd '' err
          IFS= read -rd '' out
          < <( out=$(ls /dev/null /x); 2>&1; printf '%s' "$out")


          (assuming the command doesn't output any NUL)



          Note that $err will include the trailing newline character.



          Other approaches could be to decorate the stdout and stderr differently and remove the decoration upon reading:



          out= err=
          while IFS= read -r line; do
          case $line in
          (out:*) out=$out$line#out:$'n';;
          (err:*) out=$out$line#err:$'n';;
          esac
          done < <(
          ls /dev/null/x 2>&1 |
          grep --label=err --line-buffered -H '^'
          )


          That assumes GNU grep and that the lines are short enough. With lines bigger than PIPEBUF (4K on Linux), lines of the output of the two greps could end up being mangled together in chunks.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Mar 14 at 14:13

























          answered Mar 14 at 13:43









          Stéphane Chazelas

          280k53515847




          280k53515847






















              up vote
              1
              down vote













              Well, capturing the stderr in one variable and stdout in another variable without temporary file is not easy at all.



              Here is an example that works



              func() 
              echo 'hello'
              echo 'This is an error' >&2


              result=$(
              stdout=$(func) ; 2>&1
              echo -e "mysuperuniqueseparatorn"
              echo -e "$stdoutn"
              )
              var_out=$result#*mysuperuniqueseparator$'n'
              var_err=$result%$'n'mysuperuniqueseparator*


              I'm not happy with it because it's a dirty way, redirect the stderr to stdout and put both in one variable with a separator between them and then break it down into two pieces.



              Plus :




              Obviously, this is not robust, because either the standard output or the standard error of the command could contain whatever separator string you employ.




              Taken from here http://mywiki.wooledge.org/BashFAQ/002






              share|improve this answer
























                up vote
                1
                down vote













                Well, capturing the stderr in one variable and stdout in another variable without temporary file is not easy at all.



                Here is an example that works



                func() 
                echo 'hello'
                echo 'This is an error' >&2


                result=$(
                stdout=$(func) ; 2>&1
                echo -e "mysuperuniqueseparatorn"
                echo -e "$stdoutn"
                )
                var_out=$result#*mysuperuniqueseparator$'n'
                var_err=$result%$'n'mysuperuniqueseparator*


                I'm not happy with it because it's a dirty way, redirect the stderr to stdout and put both in one variable with a separator between them and then break it down into two pieces.



                Plus :




                Obviously, this is not robust, because either the standard output or the standard error of the command could contain whatever separator string you employ.




                Taken from here http://mywiki.wooledge.org/BashFAQ/002






                share|improve this answer






















                  up vote
                  1
                  down vote










                  up vote
                  1
                  down vote









                  Well, capturing the stderr in one variable and stdout in another variable without temporary file is not easy at all.



                  Here is an example that works



                  func() 
                  echo 'hello'
                  echo 'This is an error' >&2


                  result=$(
                  stdout=$(func) ; 2>&1
                  echo -e "mysuperuniqueseparatorn"
                  echo -e "$stdoutn"
                  )
                  var_out=$result#*mysuperuniqueseparator$'n'
                  var_err=$result%$'n'mysuperuniqueseparator*


                  I'm not happy with it because it's a dirty way, redirect the stderr to stdout and put both in one variable with a separator between them and then break it down into two pieces.



                  Plus :




                  Obviously, this is not robust, because either the standard output or the standard error of the command could contain whatever separator string you employ.




                  Taken from here http://mywiki.wooledge.org/BashFAQ/002






                  share|improve this answer












                  Well, capturing the stderr in one variable and stdout in another variable without temporary file is not easy at all.



                  Here is an example that works



                  func() 
                  echo 'hello'
                  echo 'This is an error' >&2


                  result=$(
                  stdout=$(func) ; 2>&1
                  echo -e "mysuperuniqueseparatorn"
                  echo -e "$stdoutn"
                  )
                  var_out=$result#*mysuperuniqueseparator$'n'
                  var_err=$result%$'n'mysuperuniqueseparator*


                  I'm not happy with it because it's a dirty way, redirect the stderr to stdout and put both in one variable with a separator between them and then break it down into two pieces.



                  Plus :




                  Obviously, this is not robust, because either the standard output or the standard error of the command could contain whatever separator string you employ.




                  Taken from here http://mywiki.wooledge.org/BashFAQ/002







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Mar 14 at 15:23









                  smarber

                  3011213




                  3011213






















                       

                      draft saved


                      draft discarded


























                       


                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f430161%2fredirect-stderr-to-a-bash-variable-and-the-stdout-to-another-bash-variable-witho%23new-answer', 'question_page');

                      );

                      Post as a guest













































































                      Popular posts from this blog

                      How to check contact read email or not when send email to Individual?

                      Bahrain

                      Postfix configuration issue with fips on centos 7; mailgun relay