diff --git a/doc_src/doc.hdr b/doc_src/doc.hdr index 7d3f58b33..9bd3e3a5e 100644 --- a/doc_src/doc.hdr +++ b/doc_src/doc.hdr @@ -155,9 +155,7 @@ equivalent. The user can string together multiple commands into a so called pipeline. This means that the standard output of one command will be read in as standard input into the next command. This is done by separating -the commands by the pipe character (|). - -For example +the commands by the pipe character (|). For example cat foo.txt | head @@ -169,6 +167,17 @@ to combine commands through pipes, read the manual pages of the commands you want to use using the 'man' command. If you want to find out more about the 'cat' program, type man cat. +Pipes usually connect file descriptor 1 (standard output) of the first +process to file descriptor 0 (standard input) of the second +process. It is possible use a different output file descriptor by +prepending it to the pipe symbol, just like you would do with normal +IO redirections. For example: + +make fish 2|less + +will attempt to build the fish program, and any errors will be shown +using the less pager. + \subsection syntax-background Background jobs When you start a job in \c fish, \c fish itself will pause, and give diff --git a/exec.c b/exec.c index f9f922ef6..ba58fc569 100644 --- a/exec.c +++ b/exec.c @@ -142,14 +142,14 @@ static int use_fd_in_pipe( int fd, io_data_t *io ) if( !io ) return 0; - if( (io->io_mode == IO_PIPE) || - (io->io_mode == IO_BUFFER) ) + if( ( io->io_mode == IO_BUFFER ) || + ( io->io_mode == IO_PIPE ) ) { if( io->pipe_fd[0] == fd || io->pipe_fd[1] == fd ) return 1; } - + return use_fd_in_pipe( fd, io->next ); } @@ -256,7 +256,10 @@ static void handle_child_io( io_data_t *io ) free_fd( io, io->fd ); } - + /* + We don't mind if this fails, it is just a speculative close + to make sure no unexpected untracked fd causes us to fail + */ while( close(io->fd) == -1 ) { if( errno != EINTR ) @@ -323,22 +326,22 @@ static void handle_child_io( io_data_t *io ) p->pid, io->pipe_fd[io->fd] ); */ - int fd_to_dup = io->fd;//io->io_mode==IO_BUFFER?1:(io->fd?1:0); + int fd_to_dup = io->fd; - if( dup2( io->pipe_fd[fd_to_dup], io->fd ) == -1 ) + if( dup2( io->pipe_fd[fd_to_dup?1:0], io->fd ) == -1 ) { debug( 1, PIPE_ERROR ); wperror( L"dup2" ); exit(1); } - if( fd_to_dup ) + if( fd_to_dup != 0 ) { close_loop( io->pipe_fd[0]); close_loop( io->pipe_fd[1]); } else - close_loop( io->pipe_fd[fd_to_dup] ); + close_loop( io->pipe_fd[0] ); /* if( close( io[i].pipe_fd[ io->fd ] ) == -1 ) @@ -797,9 +800,11 @@ void exec( job_t *j ) pipe_write.fd=1; pipe_read.io_mode=IO_PIPE; pipe_read.pipe_fd[0] = -1; + pipe_read.pipe_fd[1] = -1; pipe_write.io_mode=IO_PIPE; pipe_read.next=0; pipe_write.next=0; + pipe_write.pipe_fd[0]=pipe_write.pipe_fd[1]=0; //fwprintf( stderr, L"Run command %ls\n", j->command ); @@ -818,6 +823,8 @@ void exec( job_t *j ) mypipe[1]=-1; skip_fork=0; + pipe_write.fd = p->pipe_fd; + /* This call is used so the global environment variable array is regenerated, if needed, before the fork. That way, we avoid a @@ -1269,7 +1276,6 @@ void exec( job_t *j ) debug( 3, L"Job is constructed" ); j->io = io_remove( j->io, &pipe_read ); - j->io = io_remove( j->io, &pipe_write ); for( tmp = block_io; tmp; tmp=tmp->next ) j->io = io_remove( j->io, tmp ); diff --git a/parser.c b/parser.c index 1268944f8..7047750bd 100644 --- a/parser.c +++ b/parser.c @@ -934,7 +934,7 @@ static void parse_job_main_loop( process_t *p, tok_get_pos( tok ) ); return; } - + p->pipe_fd = wcstol( tok_last( tok ), 0, 10 ); p->argv = list_to_char_arr( args ); p->next = calloc( 1, sizeof( process_t ) ); if( p->next == 0 ) diff --git a/proc.c b/proc.c index a892eae20..324bf4591 100644 --- a/proc.c +++ b/proc.c @@ -565,13 +565,22 @@ int job_do_notification() j->notified = 1; if( !j->skip_notification ) { - fwprintf( stdout, - L"fish: %ls %d, \'%ls\' terminated by signal %ls (%ls)", - proc_is_job?L"Job":L"Process", - proc_is_job?j->job_id:p->pid, - j->command, - sig2wcs(WTERMSIG(p->status)), - sig_description( WTERMSIG(p->status) ) ); + if( proc_is_job ) + fwprintf( stdout, + L"fish: Job %d, \'%ls\' terminated by signal %ls (%ls)", + j->job_id, + j->command, + sig2wcs(WTERMSIG(p->status)), + sig_description( WTERMSIG(p->status) ) ); + else + fwprintf( stdout, + L"fish: Process %d, \'%ls\' from job %d, \'%ls\' terminated by signal %ls (%ls)", + p->pid, + p->argv[0], + j->job_id, + j->command, + sig2wcs(WTERMSIG(p->status)), + sig_description( WTERMSIG(p->status) ) ); tputs(clr_eol,1,&writeb); fwprintf (stdout, L"\n" ); found=1; diff --git a/proc.h b/proc.h index 3bdf1db74..6e67c3773 100644 --- a/proc.h +++ b/proc.h @@ -92,6 +92,8 @@ typedef struct process{ INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c INTERNAL_BLOCK */ int type; + /** File descriptor that pipe output should bind to */ + int pipe_fd; /** true if process has completed */ volatile int completed; /** true if process has stopped */ diff --git a/tokenizer.c b/tokenizer.c index f105e96e1..01442a7db 100644 --- a/tokenizer.c +++ b/tokenizer.c @@ -38,6 +38,11 @@ */ #define INPUT_ERROR L"Invalid input" +/** + Error string for when trying to pipe from fd 0 +*/ +#define PIPE_ERROR L"Can not use fd 0 as pipe output" + /** Characters that separate tokens. They are ordered by frequency of occurrence to increase parsing speed. */ @@ -489,10 +494,6 @@ void tok_next( tokenizer *tok ) switch( *tok->buff ) { - case L'|': - tok->last_type = TOK_PIPE; - tok->buff++; - break; case L'\0': tok->last_type = TOK_END; /*fwprintf( stderr, L"End of string\n" );*/ @@ -509,6 +510,15 @@ void tok_next( tokenizer *tok ) tok->buff++; break; + case L'|': + check_size( tok, 16 ); + + tok->last[0]=L'1'; + tok->last[1]=L'\0'; + tok->last_type = TOK_PIPE; + tok->buff++; + break; + case L'>': return read_redirect( tok, 1 ); case L'<': @@ -520,14 +530,31 @@ void tok_next( tokenizer *tok ) if( iswdigit( *tok->buff ) ) { int fd = *tok->buff - L'0'; + check_size( tok, 16 ); switch( *(tok->buff+1)) { + case L'|': + { + if( fd == 0 ) + { + tok_error( tok, PIPE_ERROR ); + return; + } + + tok->buff+=2; + tok->last[0]=L'0'+fd; + tok->last[1]=L'\0'; + tok->last_type = TOK_PIPE; + return; + } + case L'>': case L'<': + { tok->buff++; read_redirect( tok, fd ); return; - + } } } read_string( tok );