how to execute terminal commands in C - c

i`m trying to simulate a terminal, and in my code almost all commands execute just fine, but the commands cd folder and cd .. when i try to execute those nothing happends, can any one help me with this
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXLEN 100
#define TRUE 1
#define FALSE 0
typedef struct command {
char *cmd; // string apenas com o comando
struct command *next; // apontador para o próximo comando
} COMMAND;
COMMAND *insert(COMMAND *list, char *cmd);
COMMAND *startList();
int strCompare(char *str1, char *str2);
int strLenght(char *str);
int main(void) {
char command[MAXLEN] = "start";
int id, return_command = 0;
COMMAND *commands = startList();
char exit_com[5] = "exit\0";
int r;
while(r = strCompare(command, exit_com)){
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL){
fprintf(stdout, "%s: ", cwd);
}
fgets(command, MAXLEN, stdin);
commands = insert(commands, command);
id = fork();
if (id == 0){
return_command = system(command);
if (return_command == -1){
printf("\nErro ao executar o comando '%s'\n", command);
exit(0);
}
COMMAND *c = commands;
while(c != NULL){
printf("%s\n", c->cmd);
c = c->next;
}
exit(0);
}
wait(id);
}
return 0;
}
int strCompare(char *str1, char *str2){
char aux = str2[0];
int i = 0;
while(aux != '\0'){
if (str1[i] != str2[i])
return 1;
aux = str2[++i];
}
return 0;
}
COMMAND *startList(){
return NULL;
}
COMMAND *insert(COMMAND *list, char *cmd){
COMMAND *newCommand = (COMMAND*) malloc(sizeof(COMMAND));
newCommand->cmd = cmd;
newCommand->next = list;
return newCommand;
}

You may like to use chdir call to change the working directory of your shell process. The child processes started by system call inherit the working directory from the parent process

It's because the system function starts a new process, so every command you run through system will be only in that process. That's why shells commonly handles commands like cd internally.

Related

I'm trying to execute read in lines from file in C in a shell environment

I can compile the code, execute it with the file as a command line argument, but nothing happens. The interactive mode function prompts as normal, but not the batchMode function.
I'm trying to read in a line, and then execute that line.
example file
date
ls -la
cd
(Without spacing between lines. I can't get the formatting right on here.)
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define bSize 1000
void driveLoop();
char *userInput(void);
void removeExit(char *original, char *subString); // removes string with substring "exit"
void batchMode(char *c);
int main(int argc, char **argv){
char *fTemp;
if (argc == 1)
driveLoop(); // calls the loop function that accepts input and executes commands.
else if (argc == 2)
batchMode(&argv[1][0]);
return 0;
}
void driveLoop(void){
char *comTokens[100];
char *tempTokens;
char *command;
char *cd;
char *cdDir;
char* cdTemp;
char cdBuf[bSize];
char checkExit[] = "exit";
for (;;){
printf("> ");
command = userInput(); // reads input
if (!*command) // allows for empty string error
break;
char *exitPtr = strstr(command, "exit"); // returns a value to a pointer if substring is found
removeExit(command, "exit");
puts(command); // updates the array after the function filter
int i = 0;
tempTokens = strtok(command, " \t\n"); // tokens are how the computer recognizes shell commands
while (tempTokens && i < 99){ // geeksforgeeks.com
comTokens[i++] = tempTokens;
tempTokens = strtok(NULL, "\t\n");
}
if (strcmp(comTokens[0], "exit") == 0) // exit if input is "exit" only
exit(0);
if(strcmp(comTokens[0], "cd") == 0){ // built in change directory command
cd = getcwd(cdBuf, sizeof(cdBuf));
cdDir = strcat(cd, "/");
cdTemp = strcat(cdDir, comTokens[1]); // cplusplus.com reference
chdir(cdTemp);
continue;
}
comTokens[i] = NULL;
pid_t cFork = fork(); // creates duplicate child process of parent
if (cFork == (pid_t) - 1){ // error check
perror("fork");
}
else if (cFork == 0) { // error codes found on cplusplus.com
execvp(comTokens[0], comTokens);
perror("exec");
}
else { // children are returned. parent executes
int status;
waitpid(cFork, &status, 0);
if (exitPtr != NULL){ // if substring exit was found, exit the program
exit(0);
}
}
}
}
char *userInput(void){ // referenced Linux man page - getline(3) (linux.die.net)
char *input = NULL;
size_t size = 0;
getline(&input, &size, stdin); // updates the size as it goes along
return input;
}
void removeExit(char *original, char *subString){ // removes exit from string
char *ex;
int len = strlen(subString);
while ((ex = strstr(original, subString))){ // Referenced from a Stack Overflow page.
*ex = '\0';
strcat(original, ex+len);
}
}
void batchMode(char *c){
char *tok[100];
char *batchTokens;
char *batchBuffer = NULL;
size_t batchSize = 0;
FILE *fp = fopen(c, "r");
unsigned int line = 1;
char buffer[bSize];
while(fgets(buffer, sizeof(buffer), fp)){
int i = 0;
char *toks = strtok(buffer, "\t\n");
while (toks && i < 99){
tok[i] = malloc (strlen(toks) + 1);
strcpy(tok[i++], toks);
toks = strtok(NULL, " \t\n");
}
tok[i] = NULL;
pid_t bFork = fork();
if (bFork == (pid_t) - 1)
perror("fork");
else if (bFork == 0){
execvp(tok[i], tok);
perror("exec");
}
else {
int status;
waitpid(bFork, &status, 0);
}
}
}
side note. This is a re-attempt from a previous question that was locked for inadequate information. I've updated my code and tried to be as detailed as possible.
I'll happily provide anything further to help answer my question.
Thank you all.
edit
I put in a fprintf to verify that it reads the file in, and it does.
First note in your input file, the last command cd isn't a system command, it is a shell built-it, so you would expect it to fail (unless handled specially).
Allocating for each token (tok[i]) as discussed with either strdup (if available) or simply malloc (strlen(toks) + 1); allows you to copy the current token to the block of memory allocated. Now each tok[i] will reference the individual token saved (instead of all pointing to the last token -- due to all being assigned the same pointer)
The biggest logic error in batchMode as your call to execvp with execvp (tok[i], tok); instead of properly providing the file to execute as tok[0]. Be mindful if the file to execute isn't in your PATH, you must provide an absolute path in your input file.
Making the changes, your batchMode could be written as follows (and removing all the unused variables and moving char *tok[100]; within the while loop so it is declared within that scope):
#include <sys/types.h>
#include <sys/wait.h>
...
void batchMode(char *c)
{
char buffer[bSize];
FILE *fp = fopen (c, "r");
if (!fp) {
perror ("fopen-c");
return;
}
while (fgets (buffer, sizeof(buffer), fp)) {
int i = 0;
char *tok[100];
char *toks = strtok(buffer, " \t\n");
while (toks && i < 99){
tok[i] = malloc (strlen(toks) + 1);
strcpy(tok[i++], toks);
toks = strtok(NULL, " \t\n");
}
tok[i] = NULL;
pid_t bFork = fork();
if (bFork == (pid_t) - 1)
perror("fork");
else if (bFork == 0){
execvp (tok[0], tok);
perror("exec");
}
else {
int status;
waitpid(bFork, &status, 0);
}
}
}
Example Input Files
I have two simple input files tested:
$ cat dat/toksfile.txt
echo hello
echo goodbye
$ cat dat/toksfile2.txt
date
ls -al dat/toksfile.txt
cd
Example Use/Output
$ ./bin/shellorbatch dat/toksfile.txt
hello
goodbye
$ ./bin/shellorbatch dat/toksfile2.txt
Tue Feb 4 23:47:00 CST 2020
-rw-r--r-- 1 david david 24 Feb 4 23:24 dat/toksfile.txt
exec: No such file or directory
Look things over and let me know if you have questions:

How can I copy directory files into another folder?

I need to copy files from a certain directory into another, I'm in the part where I allocate strtok into arrays and which I find very confusing. I have 2562 files to copy. I think I need 2D array but I always get errors. Help...
#include<stdio.h>
#include<stdlib.h>
#include<dirent.h>
#include<sys/types.h>
#include<windows.h>
#include<string.h>
char** str_split(char* a_str, const char a_delim);
DIR *dir;
struct dirent *sd;
FILE *source, *target;
int main(){
char *token;
int ctr;
char pathsource[40];
char pathtarget[40];
strcpy(pathsource,"C:\\");
strcpy(pathtarget,"C:\\");
system("pause");
dir = opendir(pathsource);
if(dir){
while( (sd=readdir(dir)) != NULL ) {
token = strtok(sd->d_name,"\n");
printf("%s\n",token);
}
closedir(dir);
}
return 0;
}
by the way, I just removed a little bit from the C:\ \ - that isn't the actual code.
If you use Windows OS, you can use command such system("copy dir1\\*.txt dir2\\"); for which parameter (command string) can be constructed as you want.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
int main(void)
{
char comstart[] = "copy";
char pathsource[] = "D:\\testdir\\";
char copypattern[] = "*.*";
char pathtarget[] = "D:\\testdir2\\";
char * command;
// building command
command = (char*) malloc(strlen(comstart) + strlen(pathsource) + strlen(copypattern) + strlen(pathtarget) + 3);
if(!command)
{
printf("Unexpected error!\n");
return 1;
}
strcpy(command, comstart);
strcat(command, " ");
strcat(command, pathsource);
strcat(command, copypattern);
strcat(command, " ");
strcat(command, pathtarget);
// command execution
int res = 0;
res = system(command);
if( res == 0)
{
printf("Files copied successfully!\n");
}
else
{
printf("Unexpected error with code %d!\n", res);
}
return 0;
}
EDIT:
Or you can use more advanced approach with Win API functions. See:
CopyFileEx function
MoveFileEx function
and other

Using execvp in C to copy files under linux

Ladies and gentlemen. I have a problem when using execvp inside a C program to copy files, it just doesn't want to work. The tar and mv commands do not work as well, and i presume that anything that has to do with creating files has some kind of problem as well. The weird thing is that commands like ls (with its options) and echo work as well when called with execvp.
Here is the code in question:
int main () {
char *curr_working_dir, *input, *inputCpy, *command, **args;
struct timeval start, end, duration;
system("clear");
while(1) {
curr_working_dir = getcwd(NULL, 0);
printf("%s $ ", curr_working_dir);
free(curr_working_dir);
fgets(input, 200, stdin);
input[strlen(input) - 1] = '\0';
inputCpy = cpyInput(input);
command = strtok(input, " ");
if(!(strcmp(command, "exit"))) {
break;
}
else if(!(strcmp(command, "cd"))) {
command = strtok(NULL, " ");
struct stat s;
if(!stat(command, &s) && S_ISDIR(s.st_mode)) {
chdir(command);
}
else {
chdir(getenv("HOME"));
}
free(inputCpy);
command = NULL;
}
else {
int pid = fork();
if(pid < 0) {
printf("Error al crear el proceso hijo\n");
}
else if(pid == 0) {
args = constructArgs(inputCpy);
execvp(command, args);
printf("execvp fallo: %s\n", strerror(errno));
}
else {
gettimeofday(&start, NULL);
wait((int*)NULL);
gettimeofday(&end, NULL);
timersub(&end, &start, &duration);
printf("Duracion del comando: %ld.%06ld\n", (long int)duration.tv_sec, (long int)duration.tv_usec);
free(inputCpy);
command = NULL;
}
}
}
return 0;
}
Here are some helper functions that i use in my program:
char **constructArgs(char *input) {
int i = 0;
char *argActual, **args;
argActual = strtok(input, " ");
args = malloc(sizeof(char *));
while(argActual != NULL) {
args[i] = malloc((strlen(argActual) + 1) * sizeof(char));
strcpy(args[i], argActual);
i += 1;
args = realloc(args, (i + 1) * sizeof(char *));
argActual = strtok(NULL, " ");
}
args[i] = NULL;
return args;
}
char *cpyInput(char *input) {
char *cpy;
cpy = malloc((strlen(input) + 1) * sizeof(char));
strcpy(cpy, input);
return cpy;
}
I use constructArgs to build an array of args to a particular program that i will call through execvp by parsing an input string containing the commands with its arguments. I can change directories, call echo, list the contents of directories, but when i try to run cp, or mv, or tar it doesn't do anything.
I have checked that i am sending the correct info to execvp; have a look at this for example:
let's say i have a file in /home/john/r1.sav and i want to copy this to another folder located in /home/john/anotherfolder. I debugged my program and this is what i am getting as the content for both the command and args arguments right before the call to execvp:
command = cp
args[0] = cp
args[1] = /home/john/r1.sav
args[2] = /home/john/anotherfolder
These are the expected inputs for execvp if you want to use cp through it. But it refuses to do anything. execvp receives as its first argument the name of the program to be executed, and an array of the arguments that the program is going to need, with the first argument in the array of arguments having the name of the program as well, which is what i'm doing. Everything works just fine with ls, or echo, but not with cp, or mv or tar.
Thank you in advance for any help you can shed on this peculiar problem.
I came across this post because I had the same issue. I don't know what the issue is, but once I declared an int for the return value it worked!
char *cmd = "cp";
char *argv[3];
argv[0] = "cp";
argv[1] = "source.c";
argv[2] = "destination.c;
int value = execvp(cmd, argv);
//Unreachable

Expanding on a C-Shell program and chdir() isn't working properly

My instructor gave us a basic shell in C to expand upon, and I'm currently working on getting the shell to change directories whenever the user enters 'cd [directory]' into the command line. I've gotten it to stop seg faulting, but it won't change directories. Can anyone tell me why it isn't working?
Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
/* Array holds arguments: args[0] is the command. */
static char *args[512];
pid_t pid;
int command_pipe[2];
#define READ 0
#define WRITE 1
int chdir(const char* path);
static int
command (int input, int first, int last)
{
int pipettes[2];
/* Invoke pipe */
pipe (pipettes);
pid = fork ();
if (pid == 0)
{
if (first == 1 && last == 0 && input == 0)
{
// 1st cmd
dup2 (pipettes[WRITE], STDOUT_FILENO);
}
else if (first == 0 && last == 0 && input != 0)
{
// Mid cmd
dup2 (input, STDIN_FILENO);
dup2 (pipettes[WRITE], STDOUT_FILENO);
}
else
{
// Last cmd
dup2 (input, STDIN_FILENO);
}
if (execvp (args[0], args) == -1)
_exit (EXIT_FAILURE); // If child fails
}
if (input != 0)
close (input);
close (pipettes[WRITE]);
// If last command, nothing more needs to be read
if (last == 1)
close (pipettes[READ]);
return pipettes[READ];
}
static void
cleanup (int n)
{
int i;
for (i = 0; i < n; ++i)
wait (NULL);
}
static int go (char *cmd, int input, int first, int last);
static char line[1024];
static int n = 0;
int
main (int argc, char* argv[])
{
while (1)
{
/* Initial Prompt */
printf ("?> ");
fflush (NULL);
/* Read in command */
if (!fgets (line, 1024, stdin))
return 0;
int input = 0;
int first = 1;
char *cmd = line;
char *next = strchr (cmd, '|'); /* Find initial '|' */
char *also = strchr (cmd, ';'); /* Find initial ';' */
char *directory = argv[1];
while (next != NULL)
{
/* 'next' points to '|' */
*next = '\0';
input = go (cmd, input, first, 0);
cmd = next + 1;
next = strchr (cmd, '|'); /* Find next '|' */
first = 0;
}
if(argv[0] == "cd"){
chdir(directory);
}
input = go (cmd, input, first, 1);
cleanup (n);
n = 0;
}
return 0;
}
static char *
skip_white_space (char *s)
{
while (isspace (*s))
++s;
return s;
}
static void
parse (char *cmd)
{
cmd = skip_white_space (cmd);
char *next = strchr (cmd, ' ');
int i = 0;
while (next != NULL)
{
next[0] = '\0';
args[i] = cmd;
++i;
cmd = skip_white_space (next + 1);
next = strchr (cmd, ' ');
}
if (cmd[0] != '\0')
{
args[i] = cmd;
next = strchr (cmd, '\n');
next[0] = '\0';
++i;
}
args[i] = NULL;
}
static int
go (char *cmd, int input, int first, int last)
{
parse (cmd);
if (args[0] != NULL)
{
if (strcmp (args[0], "exit") == 0)
exit (0);
n += 1;
return command (input, first, last);
}
return 0;
}
Your immediate problem seems to lie here:
if(argv[0] == "cd"){
chdir(directory);
I think you'll find that argv[0] is the implementation's representation of your program name, not the command you just entered, which is probably in args. Or cmd. Or somewhere.
Even once you fix that, you shouldn't be using == for string comparisons in C. One of the strcmp family is the correct way to do it.

Why when I call method, execvp fails?

If you call store_commands() method anywhere in my code, execution of the command fails for some reason.
such as my main method
int main (int argc, char * argv[]) {
char *command;
store_commands(); // problem
while ( (command = readline(" $ "))!= NULL ) { // scan stdin
rl_bind_key('\t',rl_complete);
splitCommands(&mainVars, command, argv);
}
return 0;
}
my store commands method
void store_commands() {
char *newEnv;
DIR * dir;
char *new ;
struct dirent * entry;
char *env = getenv("PATH");
do {
newEnv = strsep(&env, ":");
if(newEnv != NULL)
if(strlen(newEnv) > 0) {
dir = opendir(newEnv);
if( dir == NULL ) break;
if(flag == 1) {
flag = 0;
while((entry = readdir(dir)) != NULL) {
new = malloc(strlen(entry->d_name) + 1) ;
new = strcpy(new, entry->d_name);
commands[++count] = new; // add possible commands into an array
//printf("---%i %s\n", count ,commands[count]);
}
}
closedir(dir); // close directory
}
} while(newEnv);
}
test cases
without store_commands()
**ls**
comm[0]: 'ls' and comm[1]: '(null)' // command received here
Makefile
main.c
target
libedit.2.dylib
with store_commands()
**ls**
comm[0]: 'ls' and comm[1]: '(null)' // command received here again but....
Execution of the command is failed
: No such file or directory
You are corrupting the environment with strsep. Call strdup on your env.
A minimal example:
#include <stdlib.h>
int main ()
{
char* z = getenv("PATH"); // <---- z points to the actual env, not a copy
*z = 0; // <---- corrupt the environment
system("ls"); // <---- fail
}

Resources