Если вы не знаете оболочку .... конечно, вы не знаете, как с нее перенаправить, несмотря на то, что вы можете видеть, какое значение имеет $SHELL
, и действовать в результате:
char *shell = getenv("SHELL");
if (*shell) { /* no SHELL variable defined */
/* ... */
} else if (!strcmp(shell, "/bin/sh")) { /* bourne shell */
/* ... */
} /* ... more shells */
Несмотря на то, что вы говорите в своем вопросе, довольно необычно переименовать /bin/sh
в другую оболочку, так как сценарии оболочки используют синтаксис, который зависит от этого. Единственный известный мне случай с bash(1)
, и я видел это только в Linux (и, что примечательно, в последних версиях Solaris), но синтаксис bash(1)
является расширенным набором синтаксиса sh(1)
, что делает возможным запускать сценарии оболочки, созданные для sh(1)
. Например, переименование /bin/sh
в perl
сделает вашу систему полностью непригодной для использования, так как многие системные инструменты зависят от /bin/sh
как оболочки, совместимой с bourne.
Кстати, библиотечная функция system(3)
всегда вызывает sh(1)
в качестве интерпретатора команд, поэтому не должно быть никаких проблем с его использованием, но не существует решения для захвата вывода и его обработки родительским процессом (на самом деле родительский процесс - это sh(1)
, system(3)
fork(2)
s)
Еще одна вещь, которую вы можете сделать, - это popen(3)
процесс. Этот вызов дает вам FILE
указатель на канал процесса. Вы открываете его ввод, если вы popen(3)
записываете его, и вы выводите его вывод, если хотите, или читаете его вывод. Посмотрите подробности в руководстве, так как я не знаю, перенаправляет ли он только свой стандартный вывод или перенаправляет также стандартную ошибку (я думаю, что только перенаправляет стандартный вывод по причинам, обсуждаемым ниже, и только если вы popen(3)
его с "r"
флаг).
FILE *f_in = popen("ps aux", "r");
/* read standard output of 'ps aux' command. */
pclose(f_in); /* closes the descriptor and waits for the child to finish */
Еще одна вещь, которую вы можете сделать, - это перенаправить себя после fork(2)
звонка ребенку и перед вызовом exec(2)
(таким образом, вы можете решить, хотите ли вы только stdout
или вы также хотите stderr
перенаправить обратно Вам):
int fd[2];
int res = pipe(fd);
if (res < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
if ((res = fork()) < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (res == 0) { /* child process */
dup2(fd[1], 1); /* redirect pipe to stdout */
dup2(fd[1], 2); /* redirect pipe also to stderr */
close(fd[1]); close(fd[0]); /* we don't need these */
execvp(program, argv);
perror("execvp");
exit(EXIT_FAILURE);
} else { /* parent process */
close(fd[1]); /* we are not going to write in the pipe */
FILE *f_in = fdopen(fd[0]);
/* read standard output and standard error from program from f_in FILE descriptor */
fclose(f_in);
wait(NULL); /* wait for child to finish */
}
Вы можете увидеть полный пример этого (не читая стандартную ошибку, но ее легко добавить - вам нужно только добавить второй вызов dup2 () сверху) здесь . Программа многократно выполняет команду, которую вы передаете ей в командной строке. Он должен получить доступ к выходным данным подпроцесса для подсчета строк, так как между вызовами программа поднимается на столько строк, сколько выводится программой, чтобы сделать следующий вызов, чтобы перекрыть вывод последнего вызова. Вы можете попробовать и поиграть, внося изменения по своему усмотрению.
Примечание
В вашем примере перенаправления, когда вы используете >&
, вам нужно добавить число после амперсанда, чтобы указать, какой дескриптор вы используете dup()
. Поскольку число перед >
является необязательным, то после &
является обязательным. Итак, если вы не использовали его, подготовьтесь к получению ошибки (которую, вероятно, вы не увидите, если перенаправляете stderr
). Идея иметь два отдельных выходных дескриптора состоит в том, чтобы позволить вам перенаправить stdout
и на в то же время сохраните канал для размещения сообщений об ошибках.