Index: uspace/app/bdsh/exec.c
===================================================================
--- uspace/app/bdsh/exec.c	(revision 28ee877ef38c210fe4bb7514af358e4d4f126f50)
+++ uspace/app/bdsh/exec.c	(revision ae452016941d5b8f8cb26c7fc394ebe556a555db)
@@ -112,15 +112,27 @@
 }
 
-unsigned int try_exec(char *cmd, char **argv)
+unsigned int try_exec(char *cmd, char **argv, FILE **files)
 {
 	task_id_t tid;
 	task_exit_t texit;
 	char *tmp;
-	int rc, retval;
+	int rc, retval, i;
+	fdi_node_t file_nodes[3];
+	fdi_node_t *file_nodes_p[4];
 
 	tmp = str_dup(find_command(cmd));
 	free(found);
+	
+	for (i = 0; i < 3 && files[i] != NULL; i++) {
+		if (fnode(files[i], &file_nodes[i]) == EOK) {
+			file_nodes_p[i] = &file_nodes[i];
+		}
+		else {
+			file_nodes_p[i] = NULL;
+		}
+	}
+	file_nodes_p[i] = NULL;
 
-	rc = task_spawnv(&tid, tmp, (const char **) argv);
+	rc = task_spawnvf(&tid, tmp, (const char **) argv, file_nodes_p);
 	free(tmp);
 
Index: uspace/app/bdsh/exec.h
===================================================================
--- uspace/app/bdsh/exec.h	(revision 28ee877ef38c210fe4bb7514af358e4d4f126f50)
+++ uspace/app/bdsh/exec.h	(revision ae452016941d5b8f8cb26c7fc394ebe556a555db)
@@ -4,5 +4,5 @@
 #include <task.h>
 
-extern unsigned int try_exec(char *, char **);
+extern unsigned int try_exec(char *, char **, FILE **);
 
 #endif
Index: uspace/app/bdsh/input.c
===================================================================
--- uspace/app/bdsh/input.c	(revision 28ee877ef38c210fe4bb7514af358e4d4f126f50)
+++ uspace/app/bdsh/input.c	(revision ae452016941d5b8f8cb26c7fc394ebe556a555db)
@@ -58,5 +58,6 @@
 
 /* Private helpers */
-static int run_command(char **, cliuser_t *);
+static int run_command(char **, cliuser_t *, FILE **);
+static void print_pipe_usage(void);
 
 /* Tokenizes input from console, sees if the first word is a built-in, if so
@@ -68,4 +69,9 @@
 	int rc = 0;
 	tokenizer_t tok;
+	int i, pipe_count, processed_pipes;
+	int pipe_pos[2];
+	char **actual_cmd;
+	char *redir_from = NULL;
+	char *redir_to = NULL;
 
 	if (NULL == usr->line)
@@ -82,5 +88,83 @@
 	}
 	
-	rc = run_command(cmd, usr);
+	/* Until full support for pipes is implemented, allow for a simple case:
+	 * [from <file> |] command [| to <file>]
+	 * 
+	 * First find the pipes and check that there are no more
+	 */
+	int cmd_length = 0;
+	for (i = 0, pipe_count = 0; cmd[i] != NULL; i++, cmd_length++) {
+		if (cmd[i][0] == '|') {
+			if (pipe_count >= 2) {
+				print_pipe_usage();
+				rc = ENOTSUP;
+				goto finit;
+			}
+			pipe_pos[pipe_count] = i;
+			pipe_count++;
+		}
+	}
+	
+	actual_cmd = cmd;
+	processed_pipes = 0;
+	
+	/* Check if the first part (from <file> |) is present */
+	if (pipe_count > 0 && pipe_pos[0] == 2 && str_cmp(cmd[0], "from") == 0) {
+		/* Ignore the first three tokens (from, file, pipe) and set from */
+		redir_from = cmd[1];
+		actual_cmd = cmd + 3;
+		processed_pipes++;
+	}
+	
+	/* Check if the second part (| to <file>) is present */
+	if ((pipe_count - processed_pipes) > 0 &&
+	    pipe_pos[processed_pipes] == cmd_length - 3 &&
+	    str_cmp(cmd[cmd_length-2], "to") == 0) {
+		/* Ignore the last three tokens (pipe, to, file) and set to */
+		redir_to = cmd[cmd_length-1];
+		cmd[cmd_length-3] = NULL;
+		cmd_length -= 3;
+		processed_pipes++;
+	}
+	
+	if (processed_pipes != pipe_count) {
+		print_pipe_usage();
+		rc = ENOTSUP;
+		goto finit;
+	}
+	
+	if (actual_cmd[0] == NULL) {
+		print_pipe_usage();
+		rc = ENOTSUP;
+		goto finit;
+	}
+	
+	FILE *files[4];
+	files[0] = stdin;
+	files[1] = stdout;
+	files[2] = stderr;
+	files[3] = 0;
+	
+	if (redir_from) {
+		FILE *from = fopen(redir_from, "r");
+		if (from == NULL) {
+			printf("Cannot open file %s\n", redir_from);
+			rc = errno;
+			goto finit;
+		}
+		files[0] = from;
+	}
+	
+	if (redir_to) {
+		FILE *to = fopen(redir_to, "w");
+		if (to == NULL) {
+			printf("Cannot open file %s\n", redir_to);
+			rc = errno;
+			goto finit;
+		}
+		files[1] = to;
+	}
+	
+	rc = run_command(cmd, usr, files);
 	
 finit:
@@ -94,5 +178,15 @@
 }
 
-int run_command(char **cmd, cliuser_t *usr)
+void print_pipe_usage()
+{
+	printf("Invalid syntax!\n");
+	printf("Usage of redirection (pipes in the future):\n");
+	printf("from filename | command ...\n");
+	printf("from filename | command ... | to filename\n");
+	printf("command ... | to filename\n");
+	
+}
+
+int run_command(char **cmd, cliuser_t *usr, FILE *files[])
 {
 	int id = 0;
@@ -114,5 +208,5 @@
 
 	/* See what try_exec thinks of it */
-	return try_exec(cmd[0], cmd);
+	return try_exec(cmd[0], cmd, files);
 }
 
Index: uspace/lib/c/generic/task.c
===================================================================
--- uspace/lib/c/generic/task.c	(revision 28ee877ef38c210fe4bb7514af358e4d4f126f50)
+++ uspace/lib/c/generic/task.c	(revision ae452016941d5b8f8cb26c7fc394ebe556a555db)
@@ -101,30 +101,4 @@
 int task_spawnv(task_id_t *id, const char *path, const char *const args[])
 {
-	/* Connect to a program loader. */
-	loader_t *ldr = loader_connect();
-	if (ldr == NULL)
-		return EREFUSED;
-	
-	/* Get task ID. */
-	task_id_t task_id;
-	int rc = loader_get_task_id(ldr, &task_id);
-	if (rc != EOK)
-		goto error;
-	
-	/* Send spawner's current working directory. */
-	rc = loader_set_cwd(ldr);
-	if (rc != EOK)
-		goto error;
-	
-	/* Send program pathname. */
-	rc = loader_set_pathname(ldr, path);
-	if (rc != EOK)
-		goto error;
-	
-	/* Send arguments. */
-	rc = loader_set_args(ldr, args);
-	if (rc != EOK)
-		goto error;
-	
 	/* Send default files */
 	fdi_node_t *files[4];
@@ -150,4 +124,51 @@
 	files[3] = NULL;
 	
+	return task_spawnvf(id, path, args, files);
+}
+
+/** Create a new task by running an executable from the filesystem.
+ *
+ * This is really just a convenience wrapper over the more complicated
+ * loader API. Arguments are passed as a null-terminated array of strings.
+ * Files are passed as null-terminated array of pointers to fdi_node_t.
+ *
+ * @param id    If not NULL, the ID of the task is stored here on success.
+ * @param path  Pathname of the binary to execute.
+ * @param argv  Command-line arguments.
+ * @param files Standard files to use.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int task_spawnvf(task_id_t *id, const char *path, const char *const args[],
+    fdi_node_t *const files[])
+{
+	/* Connect to a program loader. */
+	loader_t *ldr = loader_connect();
+	if (ldr == NULL)
+		return EREFUSED;
+	
+	/* Get task ID. */
+	task_id_t task_id;
+	int rc = loader_get_task_id(ldr, &task_id);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send spawner's current working directory. */
+	rc = loader_set_cwd(ldr);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send program pathname. */
+	rc = loader_set_pathname(ldr, path);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send arguments. */
+	rc = loader_set_args(ldr, args);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send files */
 	rc = loader_set_files(ldr, files);
 	if (rc != EOK)
@@ -169,5 +190,4 @@
 	
 	return EOK;
-	
 error:
 	/* Error exit */
Index: uspace/lib/c/include/task.h
===================================================================
--- uspace/lib/c/include/task.h	(revision 28ee877ef38c210fe4bb7514af358e4d4f126f50)
+++ uspace/lib/c/include/task.h	(revision ae452016941d5b8f8cb26c7fc394ebe556a555db)
@@ -37,4 +37,5 @@
 
 #include <sys/types.h>
+#include <vfs/vfs.h>
 
 typedef uint64_t task_id_t;
@@ -51,4 +52,6 @@
 extern task_id_t task_spawn(const char *, const char *const[], int *);
 extern int task_spawnv(task_id_t *, const char *path, const char *const []);
+extern int task_spawnvf(task_id_t *, const char *path, const char *const [],
+    fdi_node_t *const []);
 extern int task_spawnl(task_id_t *, const char *path, ...);
 
