Implement subset of shell script This site had a lot of problems involving implementing various languages in interpreter tag. However, practically all of them were esoteric languages that nobody uses. Time to make an interpreter for a practical language that most of users here probably already know. Yes, it's shell script, in case you have problems reading the title (not that you have). (yes, I intentionally made this challenge, as I'm bored of languages like GolfScript and Befunge winning everything, so I put some challenge where more practical programming language have bigger chances of winning)

However, the shell script is a relatively big language, so I won't ask you to implement it. Instead, I'm going to make a small subset of shell script functionality.

The subset I decided on is the following subset:


Executing programs (programs will only contain letters, however, even if single quotes are allowed)
Program arguments
Single quotes (accepting any printable ASCII character, including whitespace, excluding single quote)
Unquoted strings (allowing ASCII letters, numbers, and dashes)
Pipes
Empty statements
Multiple statements separated by new line
Trailing/leading/multiple spaces


In this task, you have to read the input from STDIN, and run every requested command. You can safely assume POSIX-compatible operating system, so there is no need for portability with Windows, or anything like that. You can safely assume that the programs that aren't piped to other programs won't read from STDIN. You can safely assume that the commands will exist. You can safely assume that nothing else will be used. If some safe assumption is broken, you can do anything. You can safely assume at most 15 arguments, and lines below 512 characters (if you need explicit memory allocation, or something - I'm really going to give small chances of winning for C, even if they are still small). You don't have to clean up file descriptors.

You are allowed to execute programs at any point - even after receiving the full line, or after STDIN ends. Choose any approach you want.

Simple testcase that lets you test your shell (note the trailling whitespace after third command):

echo hello world
printf '%08X\n' 1234567890
'echo'   'Hello,   world!'  

echo heeeeeeelllo | sed 's/\(.\)\1\+/\1/g'
  yes|head -3
echo '\\'
echo 'foo bar baz' | sed 's/bar/BAR/' | sed 's/baz/zap/'


The program above should output following result:

hello world
499602D2
Hello,   world!
helo
y
y
y
\\
foo BAR zap


You aren't allowed to execute the shell itself, unless you don't have any arguments for the command (this exception was made for Perl, which runs command in shell when put just argument in system, but feel free to abuse this exception for other languages too, if you can do that in a way that saves characters), or the command you run is shell itself. This is probably the biggest problem in this challenge, as many languages have system functions that execute shell. Instead use language APIs that call programs directly, like subprocess module in Python. This is a good idea for security anyway, and well, you wouldn't want to create an insecure shell, would you want to? This most likely stops PHP, but there are other languages to choose anyway.

If you are going to make your program in shell script, you aren't allowed to use eval, source, or . (as in, a function, not a character). It would make the challenge too easy in my opinion.

Clever rule abuse allowed. There are lots of things I explicitly disallowed, but I'm almost sure that you are still allowed to do things I haven't though of. Sometimes I'm surprised about how people interpret my rules. Also, remember that you can do anything for anything I haven't mentioned. For example, if I try to use variables, you can wipe the hard disk (but please don't).

The shortest code wins, as this is codegolf.
C (340 bytes)

I have no experience at all in golfing, but you have to start somewhere, so here goes:

#define W m||(*t++=p,m=1);
#define C(x) continue;case x:if(m&amp;2)break;
c;m;f[2];i;char b[512],*p=b,*a[16],**t=a;main(){f[1]=1;while(~(c=getchar())){
switch(c){case 39:W m^=3;C('|')if(pipe(f))C(10)if(t-a){*t=*p=0;fork()||(dup2(
i,!dup2(f[1],1)),execvp(*a,a));f[1]-1&amp;&amp;close(f[1]);i=*f;*f=m=0;f[1]=1;p=b;t=a
;}C(32)m&amp;1?*p++=0,m=0:0;C(0)}W*p++=c;}}


I added line breaks so you won't have to scroll, but didn't include them in my count since they are without semantic significance. Those after preprocessor directives are required and were counted.

Ungolfed version

#define WORDBEGIN   mode || (*thisarg++ = pos, mode = 1);
#define CASE(x)     continue; case x: if (mode &amp; 2) break;

// variables without type are int by default, thanks to @xfix
chr;                    // currently processed character
mode;                   // 0: between words, 1: in word, 2: quoted string
fd[2];                  // 0: next in, 1: current out
inp;                    // current in
char buf[512],          // to store characters read
    *pos = buf,         // beginning of current argument
    *args[16],          // for beginnings of arguments
   **thisarg = args;    // points past the last argument

main() {                          // codegolf.stackexchange.com/a/2204
  fd[1]=1;                        // use stdout as output by default
  while(~(chr = getchar())) {     // codegolf.stackexchange.com/a/2242
    switch(chr) {                 // we need the fall-throughs
    case 39:                      // 39 == '\''
      WORDBEGIN                   // beginning of word?
      mode ^= 3;                  // toggle between 1 and 2
    CASE('|')
      if(pipe(fd))                // create pipe and fall through
    CASE(10)                      // 10 == '\n'
      if (thisarg-args) {         // any words present, execute command
        *thisarg = *pos = 0;      // unclean: pointer from integer
        //for (chr = 0; chr &lt;=  thisarg - args; ++chr)
        //  printf("args[%d] = \"%s\"\n", chr, args[chr]);
        fork() || (
          dup2(inp,!dup2(fd[1],1)),
          execvp(*args, args)
        );
        fd[1]-1 &amp;&amp; close(fd[1]);  // must close to avoid hanging suprocesses
        //inp &amp;&amp; close(inp);      // not as neccessary, would be cleaner
        inp = *fd;                // next in becomes current in
        *fd = mode = 0;           // next in is stdin
        fd[1] = 1;                // current out is stdout
        pos = buf;
        thisarg = args;
      }
    CASE(32)                      // 32 == ' '
      mode &amp; 1  ?                 // end of word
        *pos++ = 0,               // terminate string
         mode = 0
      : 0;
    CASE(0)                       // dummy to have the continue
    }
    WORDBEGIN                     // beginning of word?
    *pos++ = chr;
  }
}


Features


Parallel execution: you can type the next command while the one before is still executing.
Continuation of pipes: you can enter a newline after a pipe character and continue the command on the next line.
Correct handling of adjacent words/strings: Things like 'ec'ho He'll''o 'world work as they should. Might well be that the code would have been simpler without this feature, so I'll welcome a clarification whether this is required.


Known problems


Half the file descriptors are never closed, child processes never reaped. In the long run, this will likely cause some kind of resource exhaustion.
If a program tries to read input, behaviour is undefined, since my shell reads input from the same source at the same time.
Anything might happen if the execvp call fails, e.g. due to a mistyped program name. Then we have two processes playing at being shell simultaneously.
Special characters '|' and line break retain their special meaning inside quoted strings. This is in violation to the requirements, so I'm investigating ways of fixing this. Fixed, at a cost of about 11 bytes.


Other Notes


The thing obviously does not include a single header, so it depends on implicit declarations of all functions used. Depending on calling conventions, this might or might not be a problem.
Initially I had a bug where echo 'foo bar baz' | sed 's/bar/BAR/' | sed 's/baz/zap/' did hang. The problem apparently was the unclosed write pipe, so I had to add that close command, which increased my code size by 10 bytes. Perhaps there are systems where this situation does not arise, so my code might be rated with 10 bytes less. I don't know.
Thanks to the C golfing tips, in particular no return type for main, EOF handling and ternary operator, the last one for pointing out that ?: can have nested , without (…).