News for hackers. Stuff that matters.
Re:Bash $* and $@ (2017) by spc476 on Sunday January 12, 2020 @09:39PM (#22031409)
Here's one thing that I find difficult to remember how to do in shell: run a command, have stdout go to a file (or /dev/null or whatever), but pipe stderr to more or grep or some other program to process.

I mean, I would expect the syntax to be something like:

    make >/dev/null 2| more
but no ... it's some incomprehensible mess of redirection arcana to get that to work.
Re:Bash $* and $@ (2017) by pwg on Sunday January 12, 2020 @10:07PM (#22031522)
The syntax for that is present in the manual:

    Note that the order of redirections
    is significant.  For example, the 
    command

    ls > dirlist 2>&1

    directs both standard output and 
    standard error to the file dirlist, 
    while the command

    ls 2>&1 > dirlist

    directs only the standard output to 
    file dirlist, because the standard
    error was duplicated from the 
    standard output before the standard 
    output was redirected to dirlist.
Does it look a little arcane, yes, esp. until one memorizes it.

Can it be memorized? Yes, because it is just a single 'incantation': "2>&1". Just put that redirection operator before the redirection of the standard output to the file, and the result is stdout goes to the file, stderr goes to the pipe.

Re:Bash $* and $@ (2017) by Legogris on Monday January 13, 2020 @03:33AM (#22032799)
Just in case someone's confused by this, it becomes clear if you know how pipes work. `2>&1` means "redirect stderr to whatever stdout is currently pointed to" (redirection by copy, not by reference). More recent version of bash manual[0] has a less confusing wording in the same paragraph IMO:

  Note that the order of redirections is significant. For example, the command

  ls > dirlist 2>&1

  directs both standard output (file descriptor 1) and standard error (file descriptor 2) to the file dirlist, while the command

  ls 2>&1 > dirlist

  directs only the standard output to file dirlist, because the standard error was made a copy of the standard output before the standard output was redirected to dirlist. 

[0]: https://www.gnu.org/software/bash/manual/html_node/Redirecti...
Re:Bash $* and $@ (2017) by dllthomas on Sunday January 12, 2020 @11:14PM (#22031759)
It's actually a pretty simple model. Every process has an array of open file objects. File descriptors are indexes into that array. "Redirection" copies the underlying entries, and are processed left-to-right.

"Redirecting streams" winds up being confusing - it's all just `dup2`.

Re:Bash $* and $@ (2017) by spc476 on Monday January 13, 2020 @05:17AM (#22033249)
Okay, but can you pipe stdout to one string of commands, and stderr to a different string of commands? That's something I feel should be possible but how Unix shells handle redirection is just ... alien to my way of thinking.

Yes, I know that underneath it's all calls to `pipe()` and `dup2()`, which I can do (and have done) in a language other than shell. It's the shell redirection syntax (for anything more complex than simple redirection or a pipe) that just doesn't make sense to me.

Re:Bash $* and $@ (2017) by pwg on Monday January 13, 2020 @07:19AM (#22033773)
Yes you can (provided you have Bash and a system that supports process substitution):

    command 2>&1 > >(cat - | cat - > fd1) | cat - | cat - > fd2
Note, the extra 'cats' are just to show a "string of commands".

How to read this:

When Bash sets up the file descriptors for 'command' it initially makes descriptor 1 refer to the pipe, and descriptor 2 refer to the terminal.

So, the dup operator (2>&1) copies the fd in descriptor 1 (which is the pipe) into descriptor 2 (so after the dup operator is processed both stderr and stdout for command reference the pipe).

Next descriptor 1 is replaced (the > operator) by a reference to a fifo created by the process substitution operator (the >(...) operator).

So now command's descriptor 1 refers to the fifo created by >() (which itself contains a "string of commands").

Then, because stderr for command was made to be a copy of what was previously stdout (before modifying stdout) it continues to refer to the pipe Bash setup (the | operator), and stderr now flows out over the pipes to another "string of commands".

Re:Bash $* and $@ (2017) by dredmorbius on Sunday January 12, 2020 @11:26PM (#22031818)

    ( echo "stdout"; echo "stderr" 1>&2 ) 2>&1 1>/dev/null
Re:Bash $* and $@ (2017) by echelon on Monday January 13, 2020 @12:47AM (#22032134)
Practically alien runes.