[Linux] Passing Commands to Shell?

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
arcelios
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 18
Joined: Mon Oct 06, 2008 4:03 pm

[Linux] Passing Commands to Shell?

Post by arcelios »

All right, so there are many GUI's written for popular command-line only programs on Linux; they're written for Nmap, Memtest, etc.

So I have 2 questions:

1. What languages allow you to do this.
2. And, quite simply: how?

I know that in C++ there are "SYSTEM" commands that allow you to utilize various system calls, but can this be used to directly take user imput and turn that into something put into the command line?
and sometimes there’s a third, even deeper level and that one is the same as the top surface one...Like with pie…
-Dr Horrible
User avatar
Ginto8
ES Beta Backer
ES Beta Backer
Posts: 1064
Joined: Tue Jan 06, 2009 4:12 pm
Programming Language of Choice: C/C++, Java

Re: [Linux] Passing Commands to Shell?

Post by Ginto8 »

here's a code snippet for a way to take and execute shell commands (should work on all OS's):

Code: Select all

string command = "";

std::cout << "Enter command: ";
getline(std::cin, command);
system(command.c_str());
Quit procrastinating and make something awesome.
Ducky wrote:Give a man some wood, he'll be warm for the night. Put him on fire and he'll be warm for the rest of his life.
arcelios
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 18
Joined: Mon Oct 06, 2008 4:03 pm

Re: [Linux] Passing Commands to Shell?

Post by arcelios »

Ginto8 wrote:here's a code snippet for a way to take and execute shell commands (should work on all OS's):

Code: Select all

string command = "";

std::cout << "Enter command: ";
getline(std::cin, command);
system(command.c_str());
It's really as simple as making a "system" call?

So I could do this?

Code: Select all

system("mkdir /xyz");
and sometimes there’s a third, even deeper level and that one is the same as the top surface one...Like with pie…
-Dr Horrible
User avatar
M_D_K
Chaos Rift Demigod
Chaos Rift Demigod
Posts: 1087
Joined: Tue Oct 28, 2008 10:33 am
Favorite Gaming Platforms: PC
Programming Language of Choice: C/++
Location: UK

Re: [Linux] Passing Commands to Shell?

Post by M_D_K »

I think he is talking about piping input and output through a GUI.
Gyro Sheen wrote:you pour their inventory onto my life
IRC wrote: <sparda> The routine had a stack overflow, sorry.
<sparda> Apparently the stack was full of shit.
User avatar
MarauderIIC
Respected Programmer
Respected Programmer
Posts: 3406
Joined: Sat Jul 10, 2004 3:05 pm
Location: Maryland, USA

Re: [Linux] Passing Commands to Shell?

Post by MarauderIIC »

"I think he is talking about piping input and output through a GUI."
I have code for this that might be applicable. It's not the best, but it works. This can actually be run as a shell, although functionality is limited.

Essentially you might be looking looking for execvp, or execlp if you want to hard-code parameters.

Code dump:
shell.c (main):

Code: Select all

/************************************************************************
 * Created by Steven A. Wilson						*
 * CS 470-001 S'08 Assignment 4						*
 * This is a basic command interpreter for Linux			*
 * Specifications:							*
 * 	+ Supports background processing				*
 * 	+ Supports MAX_INPUT input chars and MAX_ARGS process arguments *
 * 	+ Supports chained pipes up to MAX_PS   			*
 * 	+ Supports output to single file				*
 ***********************************************************************/

#include "run.h"
#include "input.h"
#include "defs.h"

#include <sys/types.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char* argv[]) {

    const char* EXIT_STR = "exit";
    const char* QUIT_STR = "quit";
    const char PROMPT[] = "shell> ";
    const char DELIMS[] = ">|";

    char actual_input[MAX_INPUT];   //Stores the user's input
    char tokenize[MAX_INPUT];	//A copy of actual_input that we run strtok on
    char buffer[MAX_INPUT]; //A modifiable copy of current input btwn DELIMs
    char* pipe_input;	//Pointer past the next DELIM
    char* input;	//The current input between DELIMs
    size_t psi = 0;	//process index

    //Initialize list of processes
    Process* ps_queue[MAX_PS];
    for (int i = 0;i < MAX_PS;++i)
	ps_queue[i] = NULL;
    Process* cur_ps = ps_queue[psi];
    Process* prev_ps = NULL;

    //Get input from the user, tokenize it on pipe characters.
    //If there are no pipes, parse the whole line.
    //We have to copy the whole thing because split_str also calls strtok
    get_input(PROMPT, actual_input, MAX_INPUT);
    strncpy(tokenize, actual_input, MAX_INPUT);
    pipe_input = strtok(tokenize, DELIMS);

    input = (pipe_input) ? pipe_input : tokenize;

    while ( strlen(input) == 0 ||
	    ((strncmp(input, EXIT_STR, MAX_INPUT) != 0) &&
	     (strncmp(input, QUIT_STR, MAX_INPUT) != 0)) )
    {
	trim_str(input);

	prev_ps = cur_ps;
	cur_ps = ps_queue[psi] = init_ps(alloc_ps());

	//Copy our working input to buffer so we can modify it w/o modifying
	//the actual_input
	strncpy(buffer, input, MAX_INPUT);
	//trim_str(buffer);
	cur_ps->background = (buffer[strlen(buffer)-1] == '&');

	//Cut off the '&' if we're running in background.
	//Not storing size_t strlen(input)-1 because bkgd is uncommon
	if (cur_ps->background)
	    buffer[strlen(buffer)-1] = '\0';

	if (input-1 > 0)
	    cur_ps->file = (*(input-1) == '>');

	split_str(cur_ps->argv, buffer, " ", MAX_ARGS);

	//Move our point of reference (re: the pipe & fd), if applicable
	set_pipes(cur_ps, prev_ps);

	//Since split_str also calls strtok, we need to redo this
	//Must recopy because apparently strtok does something to the original
	strncpy(tokenize, actual_input, MAX_INPUT);
	pipe_input = strtok(tokenize, DELIMS);
	//Advance one past our current token
	for (int tok_track = 0;tok_track <= psi;++tok_track)
	    pipe_input = strtok(NULL, DELIMS);

	if (prev_ps && prev_ps->file) {
	    printf("shell: Parse error: Cannot redirect after first '>>'\n");
	    free_all(ps_queue, MAX_PS);
	    pipe_input = NULL;
	} else if ( prev_ps && (cur_ps->background || prev_ps->background) ) {
	    printf("shell: Parse error: Cannot use '&' with '|' or '>>'.\n");
	    free_all(ps_queue, MAX_PS);
	    pipe_input = NULL;
	}

	//This process has been totally initialized.
	//Move on to the next.
	if (!pipe_input) {	//No more processes to move on to
	    run_all(ps_queue, MAX_PS);
	    free_all(ps_queue, MAX_PS);
	    cur_ps = NULL;
	    prev_ps = NULL;
	    psi = 0;

	    //Reset our user input buffer
	    memset(actual_input, '\0', sizeof(actual_input));

	    //Get new input and the initial pipe val
	    get_input(PROMPT, actual_input, MAX_INPUT);
	    strncpy(tokenize, actual_input, MAX_INPUT);
	    pipe_input = strtok(tokenize, DELIMS);
	} else {
	    //Continue tokenization
	    psi++;
	}

	input = (pipe_input) ? pipe_input : tokenize;
    }

    return 0;
}
run.c

Code: Select all

/**********************
 * run.c
 * Steven A Wilson - CS 470
 * Part of shell
 * Contains function definitions for running a parsed command
 *********************/

#include "run.h"
#include "process.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>	//file i/o
#include <sys/fcntl.h>	//file i/o

/*
 * Handles the forking and does the exec call, etc
 * argv follows standard argv practice (prog is argv[0])
 * background determines whether or not parent waits for completion
 */
bool run(Process* ps, int ps_index) {

    if (ps == NULL)
	return false;

    const char CHILD_ERROR_PREFIX[] = "shell: Execution error";
    const char PARENT_ERROR_PREFIX[] = "shell: Wait error";
    
    //Options to call a wait but not actually wait for it
    //Used on background child processes
    const int NOWAIT_OPTIONS = WNOHANG | WUNTRACED | WCONTINUED;

    static int fds[MAX_PS][2];
    const bool do_pipe = (ps->pipe_right || ps->pipe_left);

    const int input_index = ps_index-1;
    const int output_index = ps_index;

    //Only open the pipe once.
    //Every process opens its output pipe
    //Input pipe is one less than the output pipe
    if (ps->pipe_right && !ps->file) {
	if (pipe(fds[output_index]) == -1) {
	    perror("pipe");
	    return false;
	}
    }

    int pid = fork();

    if (pid > 0) { //Parent
	ps->pid = pid;
	if (!do_pipe) {
	    if (!ps->background) {
		if (waitpid(pid, NULL, 0) == -1) {
		    perror(PARENT_ERROR_PREFIX);
		    return false;
		}
	    } else {
		//If we don't do a wait on all child
		//processes, they become zombies
		if (waitpid(pid, NULL, NOWAIT_OPTIONS) == -1) {
		    perror(PARENT_ERROR_PREFIX);
		    return false;
		}
	    }
	} else {
	    for (int i = 0;i <= input_index;++i) {
		close(fds[i][0]);
		close(fds[i][1]);
	    }

	    //Wait for the last child to finish
	    if (ps->pipe_left && !ps->pipe_right)
		waitpid(ps->pid, NULL, 0);
	}
    } else if (pid == 0) { //Child
	//Get our input from the pipe
	if (ps->pipe_left) {
	    dup2(fds[input_index][0], 0);
	    close(fds[input_index][1]);
	    /*
	     * Don't close unused output pipes here because 
	     * it makes the program freak out.
	     * */
	}

	//Send our output to the pipe
	if (ps->pipe_right) {
	    if (ps->file) {
		printf("shell: Parse error: Cannot have '|' after '>>'\n");
		close(fds[input_index][0]);
		close(fds[input_index][1]);
		getchar();
		exit(EXIT_FAILURE);
	    }
	    dup2(fds[output_index][1], 1);
	    close(fds[output_index][0]);
	    if (!ps->pipe_left && input_index >= 0) {
		close(fds[input_index][0]);
		close(fds[input_index][1]);
	    }
	}
	if (ps->file)
	    fds[output_index][1] = creat(ps->argv[0], 0600);

	if (!ps->file) {
	    execvp(ps->argv[0], ps->argv);
	} else {
	    char buff;
	    if (fds[output_index][1] != -1) {
		while (read(fds[input_index][0], &buff, 1) > 0)
		    write(fds[output_index][1], &buff, 1);
		close(fds[output_index][1]);
		exit(EXIT_SUCCESS); //Prevent fall-through to perror
	    }
	}

	printf("\n");
	//This will only be hit if execvp() or creat() fails.
	perror(CHILD_ERROR_PREFIX);
	if (!ps->file) {
	    printf("%s (cont): while attempting to execute '%s'\n",
		    CHILD_ERROR_PREFIX, ps->argv[0]);
	} else {
	    printf("%s (cont): while attempting to write to '%s'\n",
		    CHILD_ERROR_PREFIX, ps->argv[0]);
	}
	exit(EXIT_FAILURE);	//Kill the child
    } else { //Error
	if (ps->pipe_left || ps->pipe_right) {
	    close(fds[input_index][0]);
	    close(fds[output_index][1]);
	}
	printf("%s: fork failed\n", CHILD_ERROR_PREFIX);
	return false;
    }
    return true;
}
process.c

Code: Select all

#include "process.h"
#include "run.h"

#include <string.h>	//memset
#include <stdlib.h>	//malloc
#include <stdio.h>	//printf debug

//Set initial values for a process, returns a pointer to it.
Process* init_ps(Process* ps) {
	if (!ps)
		return NULL;
	for (int i = 0;i < MAX_ARGS;++i)
		ps->argv[i] = NULL;
	ps->background = false;
	ps->pipe_right = false;
	ps->pipe_left = false;
	ps->file = false;

	return ps;
}

//Allocate a new process, return a pointer to it
Process* alloc_ps() {
	Process* ps = (Process*) malloc( sizeof(Process) );
	if (ps == NULL) {
		printf("Failure allocating memory for process.\n");
		exit(EXIT_FAILURE);
	}
	return ps;
}

//Set cur_ps as being on RHS of a pipe and prev_ps as being on LHS of a pipe
//If either are null, abort
void set_pipes(Process* cur_ps, Process* prev_ps) {
    if (!cur_ps || !prev_ps)
	return;
    cur_ps->pipe_left = true;
    cur_ps->pipe_right = false;

    prev_ps->pipe_right = true;
}

//Runs num_ps ps's processes in arr
void run_all(Process** arr, size_t num_ps) {
	if (!arr || arr[0] == NULL)
		return;

	Process* ps;
	for (int i = 0;i < num_ps; ++i) {
		ps = arr[i];
		if (!ps)
			break;
		run(ps, i);
	}
}

//Frees num_ps ps's processes in arr
void free_all(Process** arr, size_t num_ps) {
	if (!arr || arr[0] == NULL)
		return;

	Process* ps;
	for (int i = 0;i < num_ps;++i) {
		ps = arr[i];
		if (!ps)
			break;
		for (int j = 0;j < MAX_ARGS && ps->argv[j];++j)
			free(ps->argv[j]);
		free(ps);
		arr[i] = NULL;
	}
}
input.c

Code: Select all

/**********************
 * input.c
 * Steven A Wilson - CS470
 * Part of shell
 * Contains function definitions for parsing user input
 *********************/

#include "input.h"
#include "defs.h"

#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>

//Modifies str, replacing \r\n with \0\0
void strip_rn(char str[]) {
    char* found_endline;

    //Directly modify str
    found_endline = strchr(str, '\n');
    if (found_endline)
	*found_endline = '\0';

    found_endline = strchr(str, '\r');
    if (found_endline)
	*found_endline = '\0';
}

/*
 * Splits a string on first occurence of 'on'
 * Returning results to before and after, neither
 * containing 'on'
 */
void split_str(char** result, char* input, const char* delims, size_t max_args)
{
    if (!input) {
	result[0][0] = '\0';
	return;
    }

    size_t i;	
    char* cur_token = strtok(input, delims);

    //Previous result wasn't NULL
    for (i = 0;i < max_args && cur_token != NULL;++i) {
	result[i] = (char*)malloc(MAX_INPUT);
	strncpy(result[i], trim_str(cur_token), MAX_INPUT);
	cur_token = strtok(NULL, delims);
    }

    //Set the NULL result or final result to \0
    result[i] = NULL;
}

/*
 * Trims spaces off of start and end of a string
 */
char* trim_str(char* str) {
    char ptr[strlen(str)+1];
    int i, j = 0;
    bool lead = false;

    //Remove leading spaces, copy everything else.
    for (i = 0; str[i] != '\0';++i) {
	if (lead) {
	    ptr[j++] = str[i];
	} else if (str[i] != ' ' && !lead) {
	    ptr[j++] = str[i];
	    lead = true;
	}
    }
    ptr[j] = '\0';

    //Remove trailing spaces.
    while (ptr[--j] == ' ')
	ptr[j] = '\0';

    strcpy(str, ptr);
    str = ptr;
    return str;
}

/*
 * Prints 'prompt' followed by ": "
 * Waits for and stores user input in 'buffer'
 * Maximum characters sent to 'buffer' is 'max'
 */
void get_input(const char* prompt, char* buffer, size_t max) {
    printf("%s", prompt);
    fgets(buffer, max, stdin);
    strip_rn(buffer);
}

/*********
 * copy_str
 * Copies to 'to' from 'from' not including ch
 * If ch (+ ch2, if not 0) is not found, copies entire string
 * NULL-terminates 'to' if required.
 * Returns one past from's ch/ch2, or NULL if not found
 ********/
char* copy_str(char* to, const char* from, char ch = '|', char ch2 = '\0') {
    const int len_to = strlen(to);
    const int len_from = strlen(from);
    //	const int len_match = strlen(match);
    char* until = strchr(from, ch);
    size_t max;

    //Couldn't match or 2nd char is not a match
    if (until == NULL || (ch2 && *(until+1) != ch2) ) {
	max = (len_to > len_from) ? len_to : len_from;
    } else {
	max = (size_t)(until - from - 1); //-1: Avoid ch
	until++;	//Increment for retval: advance past ch
	if (ch2)
	    until++;
    }

    strncpy(to, from, max);

    if (to[max-1] != '\0')
	to[max-1] = '\0';

    return until;
}
defs.h

Code: Select all

#ifndef DEFS_H
#define DEFS_H

#define MAX_INPUT 1028
#define MAX_ARGS 128
#define MAX_PS 128

#endif
input.h

Code: Select all

/***********************************
 * Part of shell
 * input.h
 * Contains function prototypes for editing user input
 ***********************************/

#ifndef INPUT_H
#define INPUT_H
#include <sys/types.h>

void strip_rn(char str[]);
void split_str(char** result, char* input, const char* delims, size_t max_args);
void get_input(const char* prompt, char* buffer, size_t max);
char* trim_str(char* str);

#endif
process.h

Code: Select all

#ifndef PROCESS_H
#define PROCESS_H

#include "defs.h"
#include "process.h"
#include <sys/types.h>

#ifndef NULL
#define NULL 0
#endif

struct Process {
	//int fds[2];		//0 == input, 1 == output
	char* argv[MAX_ARGS];	//Standard argv
	int pid;
	bool background;	//There was a & after argv
	bool file;		//I'm really a file that input is coming to
	bool pipe_right;	//There was a pipe on the RHS of this input
	bool pipe_left;		//			..LHS..
};

//Set initial values for a process, returns a pointer to it
Process* init_ps(Process* ps);

//Allocate a new process
Process* alloc_ps();

//If cur_ps and prev_ps, then set cur_ps as being on RHS of pipe and prev_ps
//as being on LHS of pipe.
void set_pipes(Process* cur_ps, Process* prev_ps);

//Runs all processes in arr
void run_all(Process** arr, size_t num_ps);

//Frees all processes in arr
void free_all(Process** arr, size_t num_ps);
#endif
run.h

Code: Select all

#ifndef RUN_H
#define RUN_H

#include "process.h"

/* Forks to execute process specified by ps
 * Returns true to parent on success
 * Returns false ot parent on parent fail
 * Exits child on child fail
 */
bool run(Process* ps, int ps_index);

#endif
I realized the moment I fell into the fissure that the book would not be destroyed as I had planned.
Post Reply