-1

Summary: Like we have Stdout Stderr, I would like to create Stdstatus. Stdout can be stored in array and Stdstatus can be printed for user. I didn't know that stderr could be used for other messages also. But, for the sake of implementing Stdstatus, is it possible?


I have:

SomeFunction(){
  PrintForArray
  echo "Status"
}

mapfile -t SomeArray < <(SomeFunction)

The output of PrintForArray is to be stored in SomeArray, but I also want to print some Status to the command line. The Status must not be stored in the array!

Any suggestions?


Note:

  • This might be useful: What does "3>&1 1>&2 2>&3" do in a script? - Unix & Linux Stack Exchange, but I am not sure how to use this for function

  • Status is meant to be read by the user.

  • It may or may not be stored in a log file (both suggestions are welcomed).

  • I don't want to send it to standard error as the Status is not Error, I just want the User to be informed what is going on!

Porcupine
  • 1,680
  • 2
  • 19
  • 45
  • 2
    Is "Status" meant to be read by the user? Written to some log file? In other words, your question's title says "to stdout": do you have any particular reason not to send "Status" to standard error? – fra-san May 13 '21 at 09:03
  • @fra-san Please see the Edit – Porcupine May 13 '21 at 09:41
  • 2
    If you used an ordinary redirection on the command line, would you expect to redirect the status information too? (This is another way to ask fra-san's question) Note that the standard error stream is for any kind of diagnostic messages, not just errors (a better name would have been "standard _other_ stream"). – Kusalananda May 13 '21 at 09:42
  • @Kusalananda I am sorry, I am not able to understand your question. Basically like we have Stdout Stderr, I would like to create Stdstatus. Stdout can be stored in array and Stdstatus can be printed for user. Does that clarify what I am trying to achieve? I didn't know that stderr could be used for other messages also. But, for the sake of implementing Stdstatus, is it possible? – Porcupine May 13 '21 at 09:46
  • 1
    `I don't want to send it to standard error as the Status is not Error` -- stderr is for anything that is not the actual output. It doesn't have to be error information. Diagnostic information may belong to stderr as well. So it's not about "is `Status` an error?"; it's about "is `Status` not output?". – Kamil Maciorowski May 13 '21 at 09:52
  • 1
    The reason NOT to use stderr (rather than, say, `&3` as in my answer) is if you want normal handling of stderr (e.g. display on your tty, or redirect it to a logfile or something). If you're absolutely certain that the function or whatever **isn't** going to print any warnings or error messages on stderr then stderr is OK to use for this kind of back-channel communication. – cas May 13 '21 at 10:25
  • 1
    "I just want the User to be informed what is going on!" This is why you send messages to standard error, duh. One of the reasons standard error is not affected by ordinary redirection is that the user should see the messages sent to it even if ordinary output is redirected. – Kusalananda May 14 '21 at 16:25

1 Answers1

2

mapfile reads all of stdin to an array, so you can't use the stdout of the function to display messages like "Status".

Try something like this:

$ cat map.sh 
#!/bin/bash

exec 3>&1

SomeFunction(){
  printf "%s\n" {0..3}
  echo "Status" >&3
}

mapfile -t SomeArray < <(SomeFunction)

declare -p SomeArray

Explanation:

  1. First, the script duplicates its stdout to &3.

  2. When mapfile is executed, it reads stdin from the stdout of a process substitution.

    &3 is unaffected by the process substitution, so is still available in the function, and still refers to the script's original stdout (e.g. your tty or wherever the script's output was redirected to), not the redirection.

  3. SomeFunction prints its status message to &3, not to (the redirected) stdout.

Output:

$ ./map.sh 
Status
declare -a SomeArray=([0]="0" [1]="1" [2]="2" [3]="3")

BTW, If you want to do something fancier with "&3" than just have it printed on stdout, you'll have to have the exec statement redirect it to a file or a named pipe or something.

Try either of the following, for example, and you'll find you can't redirect &3. Too late, it's already gone to the script's original stdout:

mapfile -t SomeArray < <(SomeFunction) > /dev/null

mapfile -t SomeArray < <(SomeFunction) 3> /dev/null
cas
  • 1
  • 7
  • 119
  • 185
  • BTW, see [How can I redirect all output of a script to a file and replace passwords?](https://unix.stackexchange.com/a/644862/7696) for related info about `exec` and named pipes and process substitution in bash vs sh/ksh/etc. – cas May 13 '21 at 10:06
  • I observed that your script works even if `exec 3>&1` is present just before mapfile. Could you please say why that works? – Porcupine May 13 '21 at 10:07
  • 1
    because the `exec 3>&1` happens **before** SomeFunction is executed. – cas May 13 '21 at 10:09
  • But, `exec 3>&1` if defined inside `SomeFunction` just before `echo "Status" >&3`, `Status` is not printed! Is there an explanation for this as well? – Porcupine May 13 '21 at 10:13
  • 1
    If you `exec 3>&1` inside the function, the `>&3` goes to the function's stdout (which, at this point in time is attached to the stdin of `mapfile` - and is therefore gobbled up by mapfile to become an element of the array). That's why it has to be `exec`-ed outside of the function (and before the function is called). – cas May 13 '21 at 10:16