我有一个正在开发的操作系统项目(这个问题与项目解决方案没有任何关系),它正在开发一个交互式 Unix 风格的操作系统 shell。我得到了一个要处理的文件,我已将该文件拆分为多个文件并尝试执行它。我收到与“functionX”的多个定义相关的错误......首先在这里定义。
我还没有理解如何将其他文件“#include”到 C 中的单个文件中。
错误:
gcc ./build/command.o ./build/promptInfo.o ./build/global.o ./build/shell-skeleton.o -o mishell
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `print_command':
promptInfo.c:(.text+0x0): multiple definition of `print_command'; ./build/command.o:command.c:(.text+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `free_command.localalias':
promptInfo.c:(.text+0x140): multiple definition of `free_command'; ./build/command.o:command.c:(.text+0x140): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `parse_command.localalias':
promptInfo.c:(.text+0x1c0): multiple definition of `parse_command'; ./build/command.o:command.c:(.text+0x1c0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `process_command':
promptInfo.c:(.text+0x650): multiple definition of `process_command'; ./build/command.o:command.c:(.text+0x650): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o:(.data.rel.local+0x0): multiple definition of `sysname'; ./build/command.o:(.data.rel.local+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/global.o:(.data.rel.local+0x0): multiple definition of `sysname'; ./build/command.o:(.data.rel.local+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `print_command':
shell-skeleton.c:(.text+0x0): multiple definition of `print_command'; ./build/command.o:command.c:(.text+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `free_command.localalias':
shell-skeleton.c:(.text+0x140): multiple definition of `free_command'; ./build/command.o:command.c:(.text+0x140): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `parse_command.localalias':
shell-skeleton.c:(.text+0x1c0): multiple definition of `parse_command'; ./build/command.o:command.c:(.text+0x1c0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `process_command':
shell-skeleton.c:(.text+0x650): multiple definition of `process_command'; ./build/command.o:command.c:(.text+0x650): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o:(.data.rel.local+0x0): multiple definition of `sysname'; ./build/command.o:(.data.rel.local+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `show_prompt':
shell-skeleton.c:(.text+0x720): multiple definition of `show_prompt'; ./build/promptInfo.o:promptInfo.c:(.text+0x720): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `prompt_backspace':
shell-skeleton.c:(.text+0x7c0): multiple definition of `prompt_backspace'; ./build/promptInfo.o:promptInfo.c:(.text+0x7c0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `prompt':
shell-skeleton.c:(.text+0x7f0): multiple definition of `prompt'; ./build/promptInfo.o:promptInfo.c:(.text+0x7f0): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:28: mishell] Error 1
global.c:
#include <stdbool.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <termios.h> // termios, TCSANOW, ECHO, ICANON
#include <unistd.h>
const char *sysname = "mishell";
enum return_codes {
SUCCESS = 0,
EXIT = 1,
UNKNOWN = 2,
};
struct command_t {
char *name;
bool background;
bool auto_complete;
int arg_count;
char **args;
char *redirects[3]; // in/out redirection
struct command_t *next; // for piping
};
命令.c:
#include "global.c"
/**
* Prints a command struct
* @param struct command_t *
*/
void print_command(struct command_t *command) {
int i = 0;
printf("Command: <%s>\n", command->name);
printf("\tIs Background: %s\n", command->background ? "yes" : "no");
printf("\tNeeds Auto-complete: %s\n",
command->auto_complete ? "yes" : "no");
printf("\tRedirects:\n");
for (i = 0; i < 3; i++) {
printf("\t\t%d: %s\n", i,
command->redirects[i] ? command->redirects[i] : "N/A");
}
printf("\tArguments (%d):\n", command->arg_count);
for (i = 0; i < command->arg_count; ++i) {
printf("\t\tArg %d: %s\n", i, command->args[i]);
}
if (command->next) {
printf("\tPiped to:\n");
print_command(command->next);
}
}
/**
* Release allocated memory of a command
* @param command [description]
* @return [description]
*/
int free_command(struct command_t *command) {
if (command->arg_count) {
for (int i = 0; i < command->arg_count; ++i)
free(command->args[i]);
free(command->args);
}
for (int i = 0; i < 3; ++i) {
if (command->redirects[i])
free(command->redirects[i]);
}
if (command->next) {
free_command(command->next);
command->next = NULL;
}
free(command->name);
free(command);
return 0;
}
/**
* Parse a command string into a command struct
* @param buf [description]
* @param command [description]
* @return 0
*/
int parse_command(char *buf, struct command_t *command) {
const char *splitters = " \t"; // split at whitespace
int index, len;
len = strlen(buf);
// trim left whitespace
while (len > 0 && strchr(splitters, buf[0]) != NULL) {
buf++;
len--;
}
while (len > 0 && strchr(splitters, buf[len - 1]) != NULL) {
// trim right whitespace
buf[--len] = 0;
}
// auto-complete
if (len > 0 && buf[len - 1] == '?') {
command->auto_complete = true;
}
// background
if (len > 0 && buf[len - 1] == '&') {
command->background = true;
}
char *pch = strtok(buf, splitters);
if (pch == NULL) {
command->name = (char *)malloc(1);
command->name[0] = 0;
} else {
command->name = (char *)malloc(strlen(pch) + 1);
strcpy(command->name, pch);
}
command->args = (char **)malloc(sizeof(char *));
int redirect_index;
int arg_index = 0;
char temp_buf[1024], *arg;
while (1) {
// tokenize input on splitters
pch = strtok(NULL, splitters);
if (!pch)
break;
arg = temp_buf;
strcpy(arg, pch);
len = strlen(arg);
// empty arg, go for next
if (len == 0) {
continue;
}
// trim left whitespace
while (len > 0 && strchr(splitters, arg[0]) != NULL) {
arg++;
len--;
}
// trim right whitespace
while (len > 0 && strchr(splitters, arg[len - 1]) != NULL) {
arg[--len] = 0;
}
// empty arg, go for next
if (len == 0) {
continue;
}
// piping to another command
if (strcmp(arg, "|") == 0) {
struct command_t *c = malloc(sizeof(struct command_t));
int l = strlen(pch);
pch[l] = splitters[0]; // restore strtok termination
index = 1;
while (pch[index] == ' ' || pch[index] == '\t')
index++; // skip whitespaces
parse_command(pch + index, c);
pch[l] = 0; // put back strtok termination
command->next = c;
continue;
}
// background process
if (strcmp(arg, "&") == 0) {
// handled before
continue;
}
// handle input redirection
redirect_index = -1;
if (arg[0] == '<') {
redirect_index = 0;
}
if (arg[0] == '>') {
if (len > 1 && arg[1] == '>') {
redirect_index = 2;
arg++;
len--;
} else {
redirect_index = 1;
}
}
if (redirect_index != -1) {
command->redirects[redirect_index] = malloc(len);
strcpy(command->redirects[redirect_index], arg + 1);
continue;
}
// normal arguments
if (len > 2 &&
((arg[0] == '"' && arg[len - 1] == '"') ||
(arg[0] == '\'' && arg[len - 1] == '\''))) // quote wrapped arg
{
arg[--len] = 0;
arg++;
}
command->args =
(char **)realloc(command->args, sizeof(char *) * (arg_index + 1));
command->args[arg_index] = (char *)malloc(len + 1);
strcpy(command->args[arg_index++], arg);
}
command->arg_count = arg_index;
// increase args size by 2
command->args = (char **)realloc(
command->args, sizeof(char *) * (command->arg_count += 2));
// shift everything forward by 1
for (int i = command->arg_count - 2; i > 0; --i) {
command->args[i] = command->args[i - 1];
}
// set args[0] as a copy of name
command->args[0] = strdup(command->name);
// set args[arg_count-1] (last) to NULL
command->args[command->arg_count - 1] = NULL;
return 0;
}
int process_command(struct command_t *command) {
int r;
if (strcmp(command->name, "") == 0) {
return SUCCESS;
}
if (strcmp(command->name, "exit") == 0) {
return EXIT;
}
if (strcmp(command->name, "cd") == 0) {
if (command->arg_count > 0) {
r = chdir(command->args[1]);
if (r == -1) {
printf("-%s: %s: %s\n", sysname, command->name,
strerror(errno));
}
return SUCCESS;
}
}
pid_t pid = fork();
// child
if (pid == 0) {
/// This shows how to do exec with environ (but is not available on MacOs)
// extern char** environ; // environment variables
// execvpe(command->name, command->args, environ); // exec+args+path+environ
/// This shows how to do exec with auto-path resolve
// add a NULL argument to the end of args, and the name to the beginning
// as required by exec
// TODO: do your own exec with path resolving using execv()
// do so by replacing the execvp call below
execvp(command->name, command->args); // exec+args+path
exit(0);
} else {
// TODO: implement background processes here
wait(0); // wait for child process to finish
return SUCCESS;
}
// TODO: your implementation here
printf("-%s: %s: command not found\n", sysname, command->name);
return UNKNOWN;
}
promptInfo.c:
#include "command.c"
/**
* Show the command prompt
* @return [description]
*/
int show_prompt() {
char cwd[1024], hostname[1024];
gethostname(hostname, sizeof(hostname));
getcwd(cwd, sizeof(cwd));
printf("%s@%s:%s %s$ ", getenv("USER"), hostname, cwd, sysname);
return 0;
}
void prompt_backspace() {
putchar(8); // go back 1
putchar(' '); // write empty over
putchar(8); // go back 1 again
}
/**
* Prompt a command from the user
* @param buf [description]
* @param buf_size [description]
* @return [description]
*/
int prompt(struct command_t *command) {
size_t index = 0;
char c;
char buf[4096];
static char oldbuf[4096];
// tcgetattr gets the parameters of the current terminal
// STDIN_FILENO will tell tcgetattr that it should write the settings
// of stdin to oldt
static struct termios backup_termios, new_termios;
tcgetattr(STDIN_FILENO, &backup_termios);
new_termios = backup_termios;
// ICANON normally takes care that one line at a time will be processed
// that means it will return if it sees a "\n" or an EOF or an EOL
new_termios.c_lflag &=
~(ICANON |
ECHO); // Also disable automatic echo. We manually echo each char.
// Those new settings will be set to STDIN
// TCSANOW tells tcsetattr to change attributes immediately.
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
show_prompt();
buf[0] = 0;
while (1) {
c = getchar();
// printf("Keycode: %u\n", c); // DEBUG: uncomment for debugging
// handle tab
if (c == 9) {
buf[index++] = '?'; // autocomplete
break;
}
// handle backspace
if (c == 127) {
if (index > 0) {
prompt_backspace();
index--;
}
continue;
}
if (c == 27 || c == 91 || c == 66 || c == 67 || c == 68) {
continue;
}
// up arrow
if (c == 65) {
while (index > 0) {
prompt_backspace();
index--;
}
char tmpbuf[4096];
printf("%s", oldbuf);
strcpy(tmpbuf, buf);
strcpy(buf, oldbuf);
strcpy(oldbuf, tmpbuf);
index += strlen(buf);
continue;
}
putchar(c); // echo the character
buf[index++] = c;
if (index >= sizeof(buf) - 1)
break;
if (c == '\n') // enter key
break;
if (c == 4) // Ctrl+D
return EXIT;
}
// trim newline from the end
if (index > 0 && buf[index - 1] == '\n') {
index--;
}
// null terminate string
buf[index++] = '\0';
strcpy(oldbuf, buf);
parse_command(buf, command);
// print_command(command); // DEBUG: uncomment for debugging
// restore the old settings
tcsetattr(STDIN_FILENO, TCSANOW, &backup_termios);
return SUCCESS;
}
shell-skeleton.c:
#include "promptInfo.c"
/* int process_command(struct command_t *command); */
int main() {
while (1) {
struct command_t *command = malloc(sizeof(struct command_t));
// set all bytes to 0
memset(command, 0, sizeof(struct command_t));
int code;
code = prompt(command);
if (code == EXIT) {
break;
}
code = process_command(command);
if (code == EXIT) {
break;
}
free_command(command);
}
printf("\n");
return 0;
}
.