Get grep value using execl - c

I'm trying to make a program which invokes the ls and grep system calls using exec. Specifically I have to execute ls > tmp; grep ­-c pattern < tmp in order to count the number of files that fulfill the pattern. As you can see I save the content of ls in tmp file and then I want to use grep to count the files.
Let's supose pattern = txt. I'm trying things like the following code:
char *a = "ls > tmp";
char *b = " -c ";
char *fin = " < tmp";
char *comanda;
if((comanda = malloc(strlen(pattern)+strlen(pattern)+1)) != NULL){
comanda[0] = '\0'; // ensures the memory is an empty string
strcat(comanda,b);
strcat(comanda, pattern);
strcat(comanda,fin);
} else {
return -1;
}
ret = execl("/bin/sh","sh","-c",a,NULL);
ret = execl("/bin/sh","sh","-c",comanda, NULL);
But it shows me the following error: ls: cannot access > tmp: No such file or directory. So I don't know how to get the value of grep, because the execl function does not return the value, so how can I achieve the grep value?

To get the output of a command, you need to use a pipe.
Have a look at : Connecting n commands with pipes in a shell?
You could just do:
ls | grep -c pattern
If you just want to get files with a specific pattern in filename you might want to use find
find your_path/ -name "*pattern*" | wc -l
Have a look at Grabbing output from exec to get the output of execl
Here is an example, replace the 4th argument of execl with whatever you want :)
(execl("/bin/sh", "sh", "-c", "ls > tmp; grep -c 'pattern' < tmp", (char *)NULL);)
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
pipe(fd);
if (fork() == 0)
{
close(fd[0]);
dup2(fd[1], 1);
dup2(fd[1], 2);
close(fd[1]);
execl("/bin/sh", "sh", "-c", "find your_path -name '*pattern*' | wc -l", (char *)NULL);
}
else
{
char buffer[1024] = {0};
close(fd[1]);
while (read(fd[0], buffer, sizeof(buffer)) != 0)
{
write(1, buffer, strlen(buffer));
memset (buffer, 0, sizeof(buffer));
}
}
return 0;
}

You're not allocating the correct amount of space for comanda, because you don't add the sizes of all the variables correctly. So if the size is too small, you'll write outside the array bounds when you do all the strcat, and this will cause undefined behavior.
You don't need the temporary file, you can just pipe from ls to grep. I've also added quotes around the pattern in case it contains special characters.
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
char *a = "ls | grep -c '";
char *fin = "'";
char *pattern = "foo";
char *comanda;
if((comanda = malloc(strlen(a) + strlen(pattern) + strlen(fin) +1)) != NULL){
strcpy(comanda,a);
strcat(comanda,pattern);
strcat(comanda,fin);
} else {
return -1;
}
int ret = execl("/bin/sh","sh","-c", comanda, (char*)NULL);
perror("execl"); // Can only get here if there's an error
}

Related

Heredoc with tmpfile

I am writing my version of minishell and trying to implement heredoc (<<) in C. I decided to use tmpfile - first I write data from stdin to tmpfile until I reach a delimiter, then I change program's stdin to the fd of the tmpfile with dup2 and, then, try to execute cat command with execve.
I tried to simplify the program and include all relevant functions below:
int main(int argc, char **argv, char **env)
{
t_shell shell;
ft_parse_envs_to_lst(&envs, env); // transform env to linked list
shell.tmpfile = "path to tmpfile";
while (1)
{
char *buf = readline("bash: ");
add_history(buf);
shell.std_in = dup(0);
shell.std_out = dup(1);
shell.f_in = dup(0);
shell.f_out = dup(1);
/* Token is represented by linked list. "cat << eof" translates into "cat" -> ">>" -> "eof",
with token pointing to "cat" */
t_token *token = parse_buffer(buf); // parse_buffer will return pointer to the first token
ft_execute_token(shell, token, env);
free(buf);
}
}
void ft_execute_token(t_shell *shell, t_token *token, t_envs_lst *env)
{
process_next_cmd(shell, token, env);
close(shell->f_in);
close(shell->f_out);
dup2(shell->std_in, STDIN_FILENO);
dup2(shell->std_out, STDOUT_FILENO);
close(shell->std_in);
close(shell->std_out);
}
void process_next_cmd(t_shell *shell, t_token *token, t_envs_lst *env)
{
t_token *prev = ft_get_prev_token(token); // get prev separator (for example, <<) or NULL
t_token *next = ft_get_next_token(token); // get next separator (for example, <<) or NULL
if (prev && (prev->type == DOBINP)) // "<<" will be marked as DOBINP
ft_handle_dobinp(shell, token);
if (next)
process_next_cmd(shell, next->next, env); // recursively go to the next command
if (!prev) // won't run any command on the part after "<<"" but will run "cat"
{
ft_execute_cmd(token, env); // just execve on child process (created with fork), whilst parent is waiting for child
if (next && next->type == DOBINP) // delete tmpfile
{
char **argv = malloc(sizeof(char *) * 3);
argv[0] = "/bin/rm";
argv[1] = shell->tmpfile;
argv[2] = NULL;
pid_t pid = fork();
if (pid == 0)
execve("/bin/rm", argv, NULL);
}
}
}
void handle_dobinp(t_shell *shell, t_token *token)
{
int rd;
int fd;
int buf_size;
char *buf;
fd = open(shell->tmpfile, O_TRUNC | O_CREAT | O_WRONLY, 0777);
buf_size = strlen(token->str);
buf = malloc(buf_size + 1);
printf("program: Start\n");
rd = read(STDIN_FILENO, buf, buf_size);
while (rd > 0)
{
buf[rd] = '\0';
printf("program: Looping (read %s)", buf);
if (strncmp(buf, token->str, buf_size + 1) == 0)
break ;
write(fd, buf, rd);
rd = read(STDIN_FILENO, buf, buf_size);
}
free(buf);
close(fd);
shell->f_in = open(shell->tmpfile, O_RDONLY, 0777);
dup2(shell->f_in, STDIN_FILENO);
close(shell->f_in);
}
I want to execute cat << eof command. Everything works fine but I face with the problem of repeated output (during testing) in handle_dobinp function. Also one more iteration occurs in while cycle in main with empty input (i.e. program executed empty command).
There is only one process running, so I am not sure what is the cause of this behaviour?
Update: I updated program's output according to Edwin Buck comment.
bash$ cat << eof
program: Start
foo
program: Looping (read foo
)
bar
program: Looping (read bar)
program: Looping (read
)
eof
program: Looping (read eof)
foo
bar
bash$
bash$
Improve your logging. I imagine your output is correct, but looking like
bash$ cat << eof
program: Start
foo
program: Looping (read "foo")
program: Looping (read "\n")
bar
program: Looping (read "bar")
program: Looping (read "\n")
eof
program: Looping (read "eof")
program: foo
program: bar
bash$
bash$

Writing to file only at the end of the execution in my bash shell

I am writing my own shell. I am facing a problem with the commands like C1 | C2 > file or C1 | C2 >> file. When I execute a command like ls | grep .c > a.txt , I get the result of ls | grep only when I terminate the program. But I want to get it during the execution.
Code from main.c:
if (countPipes == 1 && strstr(userInput, ">>") != NULL){
token = NULL;
resetC(cmd);
resetC(cmdPipe);
token = strtok(userInput, ">>");
char *piped = strdup(token);
token = strtok(NULL, ">>");
char *file = strdup(token);
file = skipwhite(file);
token = strtok(piped, "|");
c1 = strdup(token);
token = strtok(NULL, "|");
c2 = strdup(token);
c2 = skipwhite(c2);
splitCommands(c1, cmd);
splitCommands(c2, cmdPipe);
execPipedCommandsRed(cmd, cmdPipe, file);
memset(userInput, '\0', 1000);
}
Code from functions.c:
void execPipedCommandsRed(char **cmd, char **cmdPiped, char *file){
int pipeOne[2], status, ret_val, s;
status = pipe(pipeOne);
if (status < 0) {
exit(-1);
}
int k;
int e = dup(1);
pid_t p1, p2, w;
int s2 = dup(1);
p1 = fork();
if (p1 < 0) {
printf("Fork failed!\n");
}
if (p1 == 0) {
close(pipeOne[READ]);
dup2(pipeOne[WRITE], STDOUT_FILENO);
close(pipeOne[WRITE]);
if (execvp(cmd[0], cmd) < 0) {
perror("Lathos");
}
} else {
p2 = fork();
if (p2 < 0) {
printf("Fork failed\n");
}
if (p2 == 0) {
close(pipeOne[WRITE]);
dup2(pipeOne[READ], STDIN_FILENO);
close(pipeOne[READ]);
k = open(file, O_WRONLY| O_APPEND | O_CREAT, 0644);
if (k < 0) {
puts("error k");
}
dup2(k, 1);
close(k);
if (execvp(cmdPiped[0], cmdPiped) < 0) {
perror("Lathos!");
}
} else {
// parent is waiting
waitpid(-1, &s, WUNTRACED | WCONTINUED);
printBash();
}
}
}
When i execute a command like ls | grep .c > a.txt, i get the result of ls | grep only when i terminate the program.
The traditional POSIX shell behaviour is that stdout is line-buffered only when it goes into a terminal (isatty returns 1), otherwise it is block-buffered. See setvbuf for buffering descriptions.
If you'd like matching files to be output into stdout as they are found, instead of ls | grep .c > a.txt use the following command:
stdbuf --output=L find -maxdepth 1 -name "*.c" > a.txt
stdbuf allows to explicitly specify the desired buffering mode. And find outputs one filename per-line, unlike plain ls.
You can do ls and grep, but that is sub-optimal in the number of processes involved and that each process must have its output buffering specified explicitly:
stdbuf --output=L ls -1 | stdbuf --output=L egrep '\.c$' > a.txt
Notes:
stdbuf only affects C standard streams and some heavily optimised applications may not use C standard streams at all, so that stdbuf may have no effect.
grep .c matches anything that has character c at non-0 position. Whereas find -name "*.c" matches only files with extension .c. So does egrep '\.c$'.
ls outputs multiple files per line, but grep filtering expects one file per line. ls -1 outputs one file per line.

execlp doenst work when im giving the arguments

why the first execlp doesn't work and the second one its ok.
int main(){
int fd[2];
int i;
char** arguments = (char**) malloc(5*sizeof(char*));
char *line = (char*) malloc(15*sizeof(char));
char *cfile = (char*) malloc(15*sizeof(char));
char* token;
size_t bufsize = 0;
for(i = 0;i < 5; i++){
arguments[i] = (char*) malloc(15*sizeof(char));
}
getline(&line, &bufsize, stdin);
i = 0;
token = strtok(line,TOK_DELIM);
while( token != NULL ){
(arguments)[i] = token;
strcat((arguments)[i++],"\0");
token = strtok(NULL,TOK_DELIM_1);
}
if( !fork() ){
execlp((arguments)[1],"show",(arguments)[0],NULL);
close(fd[0]); close(fd[1]);
}
close(fd[0]); close(fd[1]);
waitpid(0,NULL,0);
if( !fork() ){
printf("Executable c file:");
scanf("%s",cfile);
execlp(cfile,"show",(arguments)[0],NULL);
close(fd[0]); close(fd[1]);
}
close(fd[0]); close(fd[1]);
waitpid(0,NULL,0);
return 0;
}
As you can see now cfile and arguments are both vuriables(dynamic char array).
To run this main you have to gcc the show file writing gcc -o show show.c
Then when you run the main you have to type (pwd | ./show) pwd is the command that i want to execute and ./show the executable file that i want to use to print the output of pwd.I save what the user types on line and then i parse the arguments on a 2d dynamic array.On the first cell of arguments(dynamic 2d array) i save the command that i want to execute and at the second one the executable file that i want to run.
This is the show file that i want to use:
int main(int argc, char *argv[]){
char *arg[4];
printf("Execute commands from other file ...\n");
arg[0] = "sh";
arg[1] = "-c";
arg[2] = argv[1];
arg[3] = NULL;
execvp ("/bin/sh", arg);
return 0;
}
And you must compile it with the same name as the executable file that you are gonna give on the main programm,for example if you gonna type :pwd | ./show
on main you must compile the show file like this : gcc -o show show.c
Also im using those libs :
stdlib
stdio
unistd
string
sys/types
errno
sys/stat
fcntl
sys/wait

Bad 'ls' Command Behavior In Custom Simple Shell

I am having an issue that is just seeming to slip past my knowledge. I am writing a simple shell to learn some systems programming for an internship coming up with Unisys. In my shell, it seems that all of the commands I am trying are working besides the ls and even now discovering the wc command. ls and wc works when I type it by itself, but if I give it arguments, it will fail to work and give me an error saying No such file or directory.
here is my code:
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#define BUF_SIZE 1024
#define DELIMS " -\r\t\n"
/****************************************************************
* Capture input from the user. Returns the input from the
* standard input file descriptor.
***************************************************************/
char * getInput (char **buffer, size_t buflen)
{
size_t bufsize = BUF_SIZE;
*buffer = malloc(sizeof(char) * bufsize + 1); // allocate space for the buffer
if (!*buffer)
{
fprintf(stderr, "Shell: buffer allocation error\n");
exit(EXIT_FAILURE);
}
printf("$$ ");
fflush(NULL);
int bytesRead = getline(&(*buffer), &bufsize, stdin);
if (bytesRead < 0)
{
printf("Getline error\n");
exit(EXIT_FAILURE);
}
return *buffer; // Not capturing return value right now
}
/****************************************************************
* Tokenize the buffer input from stdin
***************************************************************/
char ** splitLine(char *line)
{
int bufsize = BUF_SIZE;
int pos = 0;
char **tokens = malloc (sizeof(char) * BUF_SIZE + 1);
char *token;
if (!tokens)
{
fprintf(stderr, "Shell: buffer allocation error\n");
exit(EXIT_FAILURE);
}
/* Tokenize the line */
token = strtok(line, DELIMS);
while (token != NULL)
{
tokens[pos] = token;
pos++;
if (pos > bufsize)
{
bufsize += BUF_SIZE;
tokens = realloc(tokens, bufsize * sizeof(char) + 1);
if (!tokens)
{
fprintf(stderr, "Shell: buffer allocation error\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, DELIMS); // continue grabbing tokens
}
tokens[pos] = NULL;
return tokens;
}
/****************************************************************
* Main function
***************************************************************/
int main (int argc, char **argv)
{
char *buf; // buffer to hold user input from standard input stream.
pid_t pid; // Parent id of the current process
int status;
/* Loop while the user is getting input */
while (getInput(&buf, sizeof(buf)))
{
char **args = splitLine(buf);
int i = 0;
/* Print tokens just to check if we are processing them correctly */
while (1)
{
char *token = args[i++];
if (token != NULL)
printf("Token #%d: %s\n", i, token);
else
break;
}
fflush(NULL);
/* Fork and execute command in the shell */
pid = fork();
switch(pid)
{
case -1:
{
/* Failed to fork */
fprintf(stderr, "Shell cannot fork: %s\n", strerror(errno));
continue;
}
case 0:
{
/* Child so run the command */
execvp(args[0], args); // Should not ever return otherwise there was an error
fprintf(stderr, "Shell: couldn't execute %s: %s\n ", buf, strerror(errno));
exit(EX_DATAERR);
}
}
/* Suspend execution of calling process until receiving a status message from the child process
or a signal is received. On return of waitpid, status contains the termination
information about the process that exited. The pid parameter specifies the set of child
process for which to wait for */
if ((pid = waitpid(pid, &status, 0) < 0))
{
fprintf(stderr, "Shell: waitpid error: %s\n", strerror(errno));
}
free(args);
}
free(buf);
exit(EX_OK);
}
For example, I have tried the following commands with output:
ls -la (THE ISSUE)
$$ ls -la
Token #1: ls
Token #2: la
ls: la: No such file or directory
$$
wc -l (THE ISSUE)
$$ wc -l
Token #1: wc
Token #2: l
wc: l: open: No such file or directory
ls
$$ ls
Token #1: ls
Makefile driver driver.dSYM main.c main.o
$$
ps -la
$$ ps -la
Token #1: ps
Token #2: la
UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT TIME COMMAND
0 2843 2405 0 31 0 2471528 8 - Us s000 0:00.08 login
501 2845 2843 0 31 0 2463080 1268 - S s000 0:01.08 -bash
501 4549 2845 0 31 0 2454268 716 - S+ s000 0:00.01 ./driv
0 4570 4549 0 31 0 2435020 932 - R+ s000 0:00.00 ps la
$$
which which
$$ which which
Token #1: which
Token #2: which
/usr/bin/which
which -a which
$$ which -a which
Token #1: which
Token #2: a
Token #3: which
/usr/bin/which
and even finally man getline
GETLINE(3) BSD Library Functions Manual GETLINE(3)
NAME
getdelim, getline -- get a line from a stream
LIBRARY
Standard C Library (libc, -lc)
.
.
.
Can anybody help me point out why I am having this issue?
Youve added "-" as a word seperator in the DELIMS macro.
Removing it should fix your problem.
As an aside, its probably best to avoid macros where you can do so easily. Here, I would have used a const char* delims to store the separators. I usually find it easier to declare a variable close to where its used - I think that makes it easier to spot bugs and read the code.

C, pass AWK syntax as argument to execl

I want to run the following command from a C program to read the system's CPU and memory use:
ps aux|awk 'NR > 0 { cpu +=$3; ram+=$4 }; END {print cpu,ram}'
I am trying to pass it to the execl command and after that read its output:
execl("/bin/ps", "/bin/ps", "aux|awk", "'NR > 0 { cpu +=$3; ram+=$4 }; END {print cpu,ram}'",(char *) 0);
But in the terminal I am getting the following error:
ERROR: Unsupported option (BSD syntax)
I would like to know how to properly pass awk as argument to execl?
You can't do this here this way.
The problem is that you want to execute several commands. execl is for executing a single command. The statement you have is using shell syntax (notably the | )
You will have better luck combining it all up in a single string and using the system(3) call.
Instead of running awk and parsing awk's output, you can do the filtering and summation in C, which often can quickly become much more convenient. (It's about the same for the exact command you have here.)
#include <errno.h>
#include <stdio.h>
void ps_cpumem(FILE* f, double* cpu_total, double* mem_total) {
for (;;) {
char buf[2000];
if (!fgets(buf, sizeof buf, f)) {
return;
}
double cpu, mem;
if (sscanf(buf, "%*s %*s %lf %lf", &cpu, &mem) == 2) {
*cpu_total += cpu;
*mem_total += mem;
}
}
}
int main() {
errno = 0;
FILE* ps = popen("ps aux", "r");
if (!ps) {
if (errno == 0) puts("popen: memory allocation failed");
else perror("popen");
return 1;
}
double cpu = 0, mem = 0;
ps_cpumem(ps, &cpu, &mem);
int rc = pclose(ps);
if (rc == -1) return 1;
printf("%%cpu: %5.1f\n" "%%mem: %5.1f\n", cpu, mem);
return 0;
}
However, you can run the full command through popen, as it executes a shell:
FILE* output = popen("ps aux | awk 'NR > 0 { cpu +=$3; ram+=$4 }; END {print cpu,ram}'", "r");
// read from output to do with as you like
As Will suggested, popen() is what you want for capturing output for subsequent use inside your program. However, if you truly are wanting to do an exec operation, you can use the shell to do your bidding via execl():
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("%s: execl returned unexpectedly: %d", argv[0],
execl("/bin/sh", "/bin/sh", "-c",
"ps aux | awk 'NR >0 { cpu += $3; ram+=$4}; END {print cpu, ram}'",
NULL));
exit(1);
}

Resources