I am new with unix and I've got an assignemnt on college to build a simple shell in c with built in cd and kill command..
This is my code which is not working..tbh I dont understand it the best so Im not suprised it is not working.. can you help me with it? Also have no idea how I would implement kill command. thank you!
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUF_SIZE 1024
const int ARGSIZE = 20;
void execute(char*args[])
{
int pid, status;
pid = fork();
if(pid<0)
{
perror("Error forking!");
}
else if(pid > 0)
{
while(wait(&status) != pid)
continue;
}
else if(pid == 0)
{
if (execvp(args[0], args) == -1)
{
perror("Error");
}
}
}
void cd(char*directory)
{
int ret = 0;
if(directory == '\0')
directory = getenv("HOME");
ret = chdir(directory);
if(ret != 0)
fprintf(stderr,"Failed to enter directory: %s\n",directory);
else
printf("%s\n",directory);
}
int main()
{
char line[BUF_SIZE];
char *args[ARGSIZE];
int argIndex = 0;
while(1){
printf("> ");
fgets(line, BUF_SIZE, stdin);
char *token;
token = strtok(line," ");
while(token!=NULL)
{
args[argIndex]=token;
token = strtok(NULL," ");
argIndex++;
}
args[argIndex]=NULL;
if(strcmp(args[0], "quit") == 0 || strcmp(args[0], "exit") == 0)
break;
if(line== "\n")
printf("> ");
else if ((strcmp(args[0], "cd") == 0))
cd(args[1]);
else
execute(args);
}
return 0;
}
You were on the right track. There were a few subtle issues where you were not accounting for the trailing '\n' that would remain in line as the last character following whatever was entered at the prompt. Including " \n" in the delimiters used to tokenize the input with strtok will remove it, allowing valid strcmp comparisons with the final token (e.g. that is why quit and exit would not quit the application).
Other than than, there were several additional things you could do a little different/better, you could handle directories entered as e.g. '~/somedir', and similar additional checks that could be employed. I have notated most below as comments to the code.
Look over the changes below and let me know if you have any questions. There are always additional checks that can be added, etc.., but on balance your approach to the problem was pretty good. (note: some of the changes made were non-substantive, e.g. "shell> " as the prompt, instead of "> ". Just handle any of those as you wish.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
enum {ARGSIZE = 20, BUF_SIZE = 1024};
void execute (char **args);
void cd (char *directory);
int killpid (char *pitstr, int sig);
int main (void)
{
char line[BUF_SIZE] = {0};
char *args[ARGSIZE] = {NULL};
char *token;
int i, argIndex = 0;
while (1) {
argIndex = 0; /* reinitialize variables */
for (i = 0; i < ARGSIZE; i++)
args[i] = NULL;
printf ("shell> "); /* prompt */
if (fgets (line, BUF_SIZE, stdin) == NULL) {
printf ("EOF received\n");
return 0;
}
if (*line == '\n') /* Enter alone */
continue;
token = strtok (line, " \n"); /* add \n to delimiters */
while (token != NULL) {
args[argIndex] = token;
token = strtok (NULL, " \n");
argIndex++;
}
if (!argIndex) continue; /* validate at least 1 arg */
if (strcmp (args[0], "quit") == 0 || strcmp (args[0], "exit") == 0)
break;
/* handle 'cd' or 'kill' separately */
if ((strcmp (args[0], "cd") == 0))
cd (args[1]);
else if ((strcmp (args[0], "kill") == 0)) {
if (args[1]) killpid (args[1], SIGTERM);
}
else
execute (args);
}
return 0;
}
void execute (char **args)
{
int pid, status;
pid = fork ();
if (pid < 0) {
perror ("Error forking!");
return;
}
else if (pid > 0) {
while (wait (&status) != pid)
continue;
}
else if (pid == 0) {
if (execvp (args[0], args) == -1) {
perror ("Error");
}
_exit (EXIT_FAILURE);
}
}
void cd (char *directory)
{
char dir[BUF_SIZE] = {0};
if (!directory) { /* handle 'cd' */
directory = getenv ("HOME");
if (chdir (directory))
fprintf (stderr, "Failed to enter directory: %s\n", directory);
else
printf ("%s\n", directory);
return;
}
if (*directory == '~') { /* handle cd ~/stuff */
strcpy (dir, getenv ("HOME"));
strcat (dir, "/");
strcat (dir, directory + 2);
if (chdir (dir))
fprintf (stderr, "Failed to enter directory: %s\n", dir);
else
printf ("%s\n", dir);
return;
}
if (chdir (directory)) /* handle given directory */
fprintf (stderr, "Failed to enter directory: %s\n", directory);
else
printf ("%s\n", directory);
}
int killpid (char *pidstr, int sig)
{
pid_t pid = (pid_t)atoi (pidstr);
if (pid < 1) {
fprintf (stderr, "warning: requested pid < 1, ignoring\n");
return (int)pid;
}
printf (" killing pid '%d' with signal '%d'\n", (int)pid, sig);
// return kill (pid, sig);
return 0;
}
Sample Usage/Output
$ ./bin/ushell
shell> cd
/home/david
shell> cd ~/tmp
/home/david/tmp
shell> kill 18004
killing pid '18004' with signal '15'
shell>
shell> quit
Related
Write a C program that counts the number of non white-space characters in an input text file. Program takes as command argument(s) the name of the input file (and the output file with option -f). Based on the option flags, it displays the output on the standard output, or writes the output to an output file. (25 points)
The command format is as follows:
command -f inputfile outputfile
or,
command -s inputfile
-f indicates writing to an output file;
-s indicates displaying the output on the screen.
I am receiving the segmentation fault on the -s command
#include <stdio.h>
#define BLANK ' '
#define NEWLINE '\n'
int main(int argc, char *argv[])
{
FILE *infile;
char c;
int char_count=0;
FILE *outfile;
outfile = fopen(argv[3], "w");
//checks for the argc length to be within the correct perameteres
if ((argc < 3) || (argc > 4))
{
printf("Incorrect format, please try gain\n");
exit(1);
}
//checks the input file to see if it is empty
if ( (infile= fopen(argv[2], "r")) == NULL)
{
fprintf(stderr,"%s: cannot open %s \n", argv[0], argv[1]);
exit(1);
}
// count the number of charecters in infile
while ( (c = getc(infile)) != EOF)
if ((c != BLANK) && (c != NEWLINE) )
char_count++;
//checks to see if the command is -s or not, outputing the corrct message to the desired location
if (argv[1] == "-s")
{
printf("%d characters\n", char_count);
}
else
{
fprintf(outfile, "%s contains %d characters\n", argv[2], char_count);
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BLANK ' '
#define NEWLINE '\n'
int main(int argc, char *argv[])
{
FILE *infile;
FILE *outfile;
char c;
int char_count=0;
//checks for the argc length to be within the correct perameteres
if ((argc < 3) || (argc > 4))
{
printf("Incorrect format, please try gain\n");
exit(1);
}
//checks if passed second argument is valid (-f or -s)
if (strcmp(argv[1], "-f") == 0) {
if (argc < 4) {
fprintf(stderr, "Please inform output file. Usage: -f inputfile outputfile");
exit(1);
}
outfile = fopen(argv[3], "w");
}
else if (strcmp(argv[1], "-s") != 0) {
fprintf(stderr, "Parameter not recognized: %s\n", argv[1]);
exit(1);
}
//checks the input file to see if it is empty
if ( (infile= fopen(argv[2], "r")) == NULL)
{
fprintf(stderr,"%s: cannot open %s \n", argv[0], argv[1]);
exit(1);
}
// count the number of charecters in infile
while ( (c = getc(infile)) != EOF)
if ((c != BLANK) && (c != NEWLINE) )
char_count++;
//checks to see if the command is -s or not, outputing the corrct message to the desired location
if (strcmp(argv[1], "-s") == 0)
{
printf("%d characters\n", char_count);
}
else
{
fprintf(outfile, "%s contains %d characters\n", argv[2], char_count);
printf("Result written to %s\n", argv[3]);
}
return 0;
}
In a bash script I am using a many-producer single-consumer pattern. Producers are background processes writing lines into a fifo (via GNU Parallel). The consumer reads all lines from the fifo, then sorts, filters, and prints the formatted result to stdout.
However, it could take a long time until the full result is available. Producers are usually fast on the first few results but then would slow down. Here I am more interested to see chunks of data every few seconds, each sorted and filtered individually.
mkfifo fifo
parallel ... >"$fifo" &
while chunk=$(read with timeout 5s and at most 10s <"$fifo"); do
process "$chunk"
done
The loop would run until all producers are done and all input is read. Each chunk is read until there has been no new data for 5s, or until 10s have passed since the chunk was started. A chunk may also be empty if there was no new data for 10s.
I tried to make it work like this:
output=$(mktemp)
while true; do
wasTimeout=0 interruptAt=$(( $(date '+%s') + 10 ))
while true; do
IFS= read -r -t5 <>"${fifo}"
rc="$?"
if [[ "${rc}" -gt 0 ]]; then
[[ "${rc}" -gt 128 ]] && wasTimeout=1
break
fi
echo "$REPLY" >>"${output}"
if [[ $(date '+%s') -ge "${interruptAt}" ]]; then
wasTimeout=1
break
fi
done
echo '---' >>"${output}"
[[ "${wasTimeout}" -eq 0 ]] && break
done
Tried some variations of this. In the form above it reads the first chunk but then loops forever. If I use <"${fifo}" (no read/write as above) it blocks after the first chunk. Maybe all of this could be simplified with buffer and/or stdbuf? But both of them define blocks by size, not by time.
This is not a trivial problem to resolve. As I hinted, a C program (or a program in some programming language other than the shell) is probably the best solution. Some of the complicating factors are:
Reading with timeouts.
If data arrives soon enough, the timeout changes.
Different systems have different sets of interval timing functions:
alarm() is likely available everywhere, but has only 1-second resolution which is liable to accumulated rounding errors. (Compile this version with make UFLAGS=-DUSE_ALARM; on macOS, use make UFLAGS=-DUSE_ALARM LDLIB2=.)
setitimer()
uses microsecond timing and the struct timeval type. (Compile this version with make UFLAGS=-DUSE_SETITIMER; on macOS, compile with make UFLAGS=-DUSE_SETITIMER LDLIB2=.)
timer_create() and
timer_settime() etc use the modern nanosecond type struct timespec. This is available on Linux; it is not available on macOS 10.14.5 Mojave or earlier. (Compile this version with make; it won't work on macOS.)
The program usage message is:
$ chunker79 -h
Usage: chunker79 [-hvV][-c chunk][-d delay][-f file]
-c chunk Maximum time to wait for data in a chunk (default 10)
-d delay Maximum delay after line read (default: 5)
-f file Read from file instead of standard input
-h Print this help message and exit
-v Verbose mode: print timing information to stderr
-V Print version information and exit
$
This code is available in my SOQ (Stack Overflow Questions) repository on GitHub as file chunker79.c in the src/so-5631-4784 sub-directory. You will need some of the support code from the src/libsoq directory too.
/*
#(#)File: chunker79.c
#(#)Purpose: Chunk Reader for SO 5631-4784
#(#)Author: J Leffler
#(#)Copyright: (C) JLSS 2019
*/
/*TABSTOP=4*/
/*
** Problem specification from the Stack Overflow question
**
** In a bash script I am using a many-producer single-consumer pattern.
** Producers are background processes writing lines into a fifo (via GNU
** Parallel). The consumer reads all lines from the fifo, then sorts,
** filters, and prints the formatted result to stdout.
**
** However, it could take a long time until the full result is
** available. Producers are usually fast on the first few results but
** then would slow down. Here I am more interested to see chunks of
** data every few seconds, each sorted and filtered individually.
**
** mkfifo fifo
** parallel ... >"$fifo" &
** while chunk=$(read with timeout 5s and at most 10s <"$fifo"); do
** process "$chunk"
** done
**
** The loop would run until all producers are done and all input is
** read. Each chunk is read until there has been no new data for 5s, or
** until 10s have passed since the chunk was started. A chunk may also
** be empty if there was no new data for 10s.
*/
/*
** Analysis
**
** 1. If no data arrives at all for 10 seconds, then the program should
** terminate producing no output. This timeout is controlled by the
** value of time_chunk in the code.
** 2. If data arrives more or less consistently, then the collection
** should continue for 10s and then finish. This timeout is also
** controlled by the value of time_chunk in the code.
** 3. If a line of data arrives before 5 seconds have elapsed, and no
** more arrives for 5 seconds, then the collection should finish.
** (If the first line arrives after 5 seconds and no more arrives
** for more than 5 seconds, then the 10 second timeout cuts in.)
** This timeout is controlled by the value of time_delay in the code.
** 4. This means that we want two separate timers at work:
** - Chunk timer (started when the program starts).
** - Delay timer (started each time a line is read).
**
** It doesn't matter which timer goes off, but further timer signals
** should be ignored. External signals will confuse things; tough!
**
** -- Using alarm(2) is tricky because it provides only one time, not two.
** -- Using getitimer(2), setitimer(2) uses obsolescent POSIX functions,
** but these are available on macOS.
** -- Using timer_create(2), timer_destroy(2), timer_settime(2),
** timer_gettime(2) uses current POSIX function but is not available
** on macOS.
*/
#include "posixver.h"
#include "stderr.h"
#include "timespec_io.h"
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
#ifdef USE_SETITIMER
#include "timeval_math.h"
#include "timeval_io.h"
#include <sys/time.h>
#endif /* USE_SETITIMER */
static const char optstr[] = "hvVc:d:f:";
static const char usestr[] = "[-hvV][-c chunk][-d delay][-f file]";
static const char hlpstr[] =
" -c chunk Maximum time to wait for data in a chunk (default 10)\n"
" -d delay Maximum delay after line read (default: 5)\n"
" -f file Read from file instead of standard input\n"
" -h Print this help message and exit\n"
" -v Verbose mode: print timing information to stderr\n"
" -V Print version information and exit\n"
;
static struct timespec time_delay = { .tv_sec = 5, .tv_nsec = 0 };
static struct timespec time_chunk = { .tv_sec = 10, .tv_nsec = 0 };
static struct timespec time_start;
static bool verbose = false;
static void set_chunk_timeout(void);
static void set_delay_timeout(void);
static void cancel_timeout(void);
static void alarm_handler(int signum);
// Using signal() manages to set SA_RESTART on a Mac.
// This is allowed by standard C and POSIX, sadly.
// signal(SIGALRM, alarm_handler);
#if defined(USE_ALARM)
static void set_chunk_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
alarm(time_chunk.tv_sec);
struct sigaction sa;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGALRM, &sa, NULL);
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
static void set_delay_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
unsigned time_left = alarm(0);
if (time_left > time_delay.tv_sec)
alarm(time_delay.tv_sec);
else
alarm(time_left);
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
static void cancel_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
alarm(0);
signal(SIGALRM, SIG_IGN);
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
#elif defined(USE_SETITIMER)
static inline struct timeval cvt_timespec_to_timeval(struct timespec ts)
{
return (struct timeval){ .tv_sec = ts.tv_sec, .tv_usec = ts.tv_nsec / 1000 };
}
static void set_chunk_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
struct itimerval tv_new = { { 0, 0 }, { 0, 0 } };
tv_new.it_value = cvt_timespec_to_timeval(time_chunk);
struct itimerval tv_old;
if (setitimer(ITIMER_REAL, &tv_new, &tv_old) != 0)
err_syserr("failed to set interval timer: ");
struct sigaction sa;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGALRM, &sa, NULL);
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
static void set_delay_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
struct itimerval tv_until;
if (getitimer(ITIMER_REAL, &tv_until) != 0)
err_syserr("failed to set interval timer: ");
struct timeval tv_delay = cvt_timespec_to_timeval(time_delay);
if (verbose)
{
char buff1[32];
fmt_timeval(&tv_delay, 6, buff1, sizeof(buff1));
char buff2[32];
fmt_timeval(&tv_until.it_value, 6, buff2, sizeof(buff2));
err_remark("---- %s(): delay %s, left %s\n", __func__, buff1, buff2);
}
if (cmp_timeval(tv_until.it_value, tv_delay) <= 0)
{
if (verbose)
err_remark("---- %s(): no need for delay timer\n", __func__);
}
else
{
struct itimerval tv_new = { { 0, 0 }, { 0, 0 } };
tv_new.it_value = cvt_timespec_to_timeval(time_delay);
struct itimerval tv_old;
if (setitimer(ITIMER_REAL, &tv_new, &tv_old) != 0)
err_syserr("failed to set interval timer: ");
if (verbose)
err_remark("---- %s(): set delay timer\n", __func__);
}
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
static void cancel_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
struct itimerval tv_new =
{
.it_value = { .tv_sec = 0, .tv_usec = 0 },
.it_interval = { .tv_sec = 0, .tv_usec = 0 },
};
struct itimerval tv_old;
if (setitimer(ITIMER_REAL, &tv_new, &tv_old) != 0)
err_syserr("failed to set interval timer: ");
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
#else /* USE_TIMER_GETTIME */
#include "timespec_math.h"
static timer_t t0 = { 0 };
static void set_chunk_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
struct sigevent ev =
{
.sigev_notify = SIGEV_SIGNAL,
.sigev_signo = SIGALRM,
.sigev_value.sival_int = 0,
.sigev_notify_function = 0,
.sigev_notify_attributes = 0,
};
if (timer_create(CLOCK_REALTIME, &ev, &t0) < 0)
err_syserr("failed to create a timer: ");
struct itimerspec it =
{
.it_interval = { .tv_sec = 0, .tv_nsec = 0 },
.it_value = time_chunk,
};
struct itimerspec ot;
if (timer_settime(t0, 0, &it, &ot) != 0)
err_syserr("failed to activate timer: ");
struct sigaction sa;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGALRM, &sa, NULL);
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
static void set_delay_timeout(void)
{
if (verbose)
err_remark("-->> %s()\n", __func__);
struct itimerspec time_until;
if (timer_gettime(t0, &time_until) != 0)
err_syserr("failed to set per-process timer: ");
char buff1[32];
fmt_timespec(&time_delay, 6, buff1, sizeof(buff1));
char buff2[32];
fmt_timespec(&time_until.it_value, 6, buff2, sizeof(buff2));
err_remark("---- %s(): delay %s, left %s\n", __func__, buff1, buff2);
if (cmp_timespec(time_until.it_value, time_delay) <= 0)
{
if (verbose)
err_remark("---- %s(): no need for delay timer\n", __func__);
}
else
{
struct itimerspec time_new =
{
.it_interval = { .tv_sec = 0, .tv_nsec = 0 },
.it_value = time_delay,
};
struct itimerspec time_old;
if (timer_settime(t0, 0, &time_new, &time_old) != 0)
err_syserr("failed to set per-process timer: ");
if (verbose)
err_remark("---- %s(): set delay timer\n", __func__);
}
if (verbose)
err_remark("<<-- %s()\n", __func__);
}
static void cancel_timeout(void)
{
if (timer_delete(t0) != 0)
err_syserr("failed to delete timer: ");
}
#endif /* Timing mode */
/* Writing to stderr via err_remark() is not officially supported */
static void alarm_handler(int signum)
{
assert(signum == SIGALRM);
if (verbose)
err_remark("---- %s(): signal %d\n", __func__, signum);
}
static void read_chunks(FILE *fp)
{
size_t num_data = 0;
size_t max_data = 0;
struct iovec *data = 0;
size_t buflen = 0;
char *buffer = 0;
ssize_t length;
size_t chunk_len = 0;
clock_gettime(CLOCK_REALTIME, &time_start);
set_chunk_timeout();
while ((length = getline(&buffer, &buflen, fp)) != -1)
{
if (num_data >= max_data)
{
size_t new_size = (num_data * 2) + 2;
void *newspace = realloc(data, new_size * sizeof(data[0]));
if (newspace == 0)
err_syserr("failed to allocate %zu bytes data: ", new_size * sizeof(data[0]));
data = newspace;
max_data = new_size;
}
data[num_data].iov_base = buffer;
data[num_data].iov_len = length;
num_data++;
if (verbose)
err_remark("Received line %zu\n", num_data);
chunk_len += length;
buffer = 0;
buflen = 0;
set_delay_timeout();
}
cancel_timeout();
if (chunk_len > 0)
{
if ((length = writev(STDOUT_FILENO, data, num_data)) < 0)
err_syserr("failed to write %zu bytes to standard output: ", chunk_len);
else if ((size_t)length != chunk_len)
err_error("failed to write %zu bytes to standard output "
"(short write of %zu bytes)\n", chunk_len, (size_t)length);
}
if (verbose)
err_remark("---- %s(): data written (%zu bytes)\n", __func__, length);
for (size_t i = 0; i < num_data; i++)
free(data[i].iov_base);
free(data);
free(buffer);
}
int main(int argc, char **argv)
{
const char *name = "(standard input)";
FILE *fp = stdin;
err_setarg0(argv[0]);
err_setlogopts(ERR_MICRO);
int opt;
while ((opt = getopt(argc, argv, optstr)) != -1)
{
switch (opt)
{
case 'c':
if (scn_timespec(optarg, &time_chunk) != 0)
err_error("Failed to convert '%s' into a time value\n", optarg);
break;
case 'd':
if (scn_timespec(optarg, &time_delay) != 0)
err_error("Failed to convert '%s' into a time value\n", optarg);
break;
case 'f':
if ((fp = fopen(optarg, "r")) == 0)
err_syserr("Failed to open file '%s' for reading: ", optarg);
name = optarg;
break;
case 'h':
err_help(usestr, hlpstr);
/*NOTREACHED*/
case 'v':
verbose = true;
break;
case 'V':
err_version("CHUNKER79", &"#(#)$Revision$ ($Date$)"[4]);
/*NOTREACHED*/
default:
err_usage(usestr);
/*NOTREACHED*/
}
}
if (optind != argc)
err_usage(usestr);
if (verbose)
{
err_remark("chunk: %3lld.%09ld\n", (long long)time_chunk.tv_sec, time_chunk.tv_nsec);
err_remark("delay: %3lld.%09ld\n", (long long)time_delay.tv_sec, time_delay.tv_nsec);
err_remark("file: %s\n", name);
}
read_chunks(fp);
return 0;
}
My SOQ repository also has a script gen-data.sh which makes use of some custom programs to generate a data stream such as this (the seed value is written to standard error, not standard output):
$ gen-data.sh
# Seed: 1313715286
2019-06-03 23:04:16.653: Zunmieoprri Rdviqymcho 5878 2017-03-29 03:59:15 Udransnadioiaeamprirteo
2019-06-03 23:04:18.525: Rndflseoevhgs Etlaevieripeoetrnwkn 9500 2015-12-18 10:49:15 Ebyrcoebeezatiagpleieoefyc
2019-06-03 23:04:20.526: Nrzsuiakrooab Nbvliinfqidbujoops 1974 2020-05-13 08:05:14 Lgithearril
2019-06-03 23:04:21.777: Eeagop Aieneose 6533 2016-11-06 22:51:58 Aoejlwebbssroncmeovtuuueigraa
2019-06-03 23:04:23.876: Izirdoeektau Atesltiybysaclee 4557 2020-09-13 02:24:46 Igrooiaauiwtna
2019-06-03 23:04:26.145: Yhioit Eamrexuabagsaraiw 9703 2014-09-13 07:44:12 Dyiiienglolqopnrbneerltnmsdn
^C
$
When fed into chunker79 with default options, I get output like:
$ gen-data.sh | chunker79
# Seed: 722907235
2019-06-03 23:06:20.570: Aluaezkgiebeewal Oyvahee 1022 2015-08-12 07:45:54 Weuababeeduklleym
2019-06-03 23:06:24.100: Gmujvoyevihvoilc Negeiiuvleem 8196 2015-08-29 21:15:15 Nztkrvsadeoeagjgoyotvertavedi
$
If you analyze the time intervals (look at the first two fields in the output lines), that output meets the specification. A still more detailed analysis is shown by:
$ timecmd -mr -- gen-data.sh | timecmd -mr -- chunker79
2019-06-03 23:09:14.246 [PID 57159] gen-data.sh
2019-06-03 23:09:14.246 [PID 57160] chunker79
# Seed: -1077610201
2019-06-03 23:09:14.269: Woreio Rdtpimvoscttbyhxim 7893 2017-03-12 12:46:57 Uywaietirkekes
2019-06-03 23:09:16.939: Uigaba Nzoxdeuisofai 3630 2017-11-16 09:28:59 Jnsncgoesycsevdscugoathusaoq
2019-06-03 23:09:17.845: Sscreua Aloaoonnsuur 5163 2016-08-13 19:47:15 Injhsiifqovbnyeooiimitaaoir
2019-06-03 23:09:19.272 [PID 57160; status 0x0000] - 5.026s - chunker79
2019-06-03 23:09:22.084 [PID 57159; status 0x8D00] - 7.838s - gen-data.sh
$
There is a noticeable pause in this setup between when the output from chunker79 appears and when gen-data.sh completes. That's due to Bash waiting on all processes in the pipeline to complete, and gen-data.sh doesn't complete until the next time it writes to the pipe after the message that finishes chunker79. This is an artefact of this test setup; it wouldn't be a factor in the shell script outlined in the question.
I would consider writing a safe multi-threaded program with queues.
I know Java better, but there might be more modern suitable languages like Go and Kotlin.
Something like this:
#!/usr/bin/perl
$timeout = 3;
while(<STDIN>) {
# Make sure there is some input
push #out,$_;
eval {
local $SIG{ALRM} = sub { die };
alarm $timeout;
while(<STDIN>) {
alarm $timeout;
push #out,$_;
}
alarm 0;
};
system "echo","process",#out;
}
GNU Parallel 20200122 introduced --blocktimeout (--bt):
find ~ | parallel -j3 --bt 2s --pipe wc
This works like normal GNU Parallel except if it takes > 2 seconds to fill a block. In that case the block read so far is simply passed to wc (unless it is empty).
It has a slightly odd startup behaviour: You have to wait 3*2s (jobslots*timeout) before the output stabilizes, and you get an output at least every 2s.
I am trying to create a function which runs one command and then pipes the output to a second command and runs that. I'm running the function in an infinite loop. The problem is, the function works the first time, but doesn't show anything after that.
For example, when I run ls | wc -l, the first time it shows the correct results, but when I run it after that I get no output.
Here is my function (parsing is handled in another function):
void system_pipe(std::string command1, std::string command2)
{
int status;
int fd[2];
int fd2[2];
pipe(fd);
int pid = fork();
// Child process.
if (pid == 0)
{
std::shared_ptr<char> temp = string_to_char(command1);
char *name[] = {"/bin/bash", "-c", temp.get(), NULL};
close(fd[0]);
dup2(fd[1], 1);
execvp(name[0], name);
exit(EXIT_FAILURE);
}
// Parent process.
else
{
std::shared_ptr<char> temp = string_to_char(command2);
char *name[] = {"/bin/bash", "-c", temp.get(), NULL};
close(fd[1]);
dup2(fd[0], 0);
waitpid(pid, &status, 0);
//my_system(command2);
// Fork and exec a new process here.
int pid2 = fork();
if (pid2 == 0)
{
execvp(name[0], name);
exit(EXIT_FAILURE);
}
else
{
waitpid(pid2, NULL, 0);
}
}
if (status)
std::cout << "Bad" << std::endl;
}
I call the function like this:
while(true)
{
string line;
getline(cin, line);
pair<string, string> commands = parse(line);
system_pipe(commands.first, commands.second);
}
Why is the function only working correctly on the first loop? What is changing after that?
else
{
std::shared_ptr<char> temp = string_to_char(command2);
char *name[] = {"/bin/bash", "-c", temp.get(), NULL};
close(fd[1]);
dup2(fd[0], 0);
waitpid(pid, &status, 0);
//my_system(command2);
That was not your intention, I believe. dup2 had to be called in child.
int pid2 = fork();
if (pid2 == 0)
{
dup2(fd[0], 0); // here.
execvp(name[0], name);
exit(EXIT_FAILURE);
}
The second problem is that you leave your pipe files open.
This is not a good coding sample, but it's only to illustrate how it should work.
// ( g++ -std=c++11 )
// type `ls | grep file.cxx`
#include <iostream>
#include <string>
#include <cstring>
#include <array>
#include <memory>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void replace_with ( std::string command )
{
char exec_name [] = "bash" , arg [] = "-c" ;
char * line [] = { exec_name , arg , &command[ 0 ] , nullptr } ;
execvp( exec_name , line ) ;
}
void pipeline ( const std::string& command0 , const std::string& command1 )
{
std::array< int , 2 > pipe_fd ; enum { READ_END , WRITE_END } ;
if ( pipe( pipe_fd.data() ) ) throw std::runtime_error( "can't create a pipe" ) ;
int id = fork() ;
if ( id < 0 ) { for ( auto each : pipe_fd ) close( each ) ;
throw std::runtime_error( "can't create a child" ) ; }
if ( ! id ) /* child */
{
close( pipe_fd[ READ_END ] ) ;
dup2( pipe_fd[ WRITE_END ] , STDOUT_FILENO ) ;
close( pipe_fd[ WRITE_END ] ) ; //
replace_with( command0 ) ;
}
else /* parent */
{
close( pipe_fd[ WRITE_END ] ) ;
waitpid( id , nullptr , 0 ) ;
int id_second = fork () ;
if ( id_second > 0 ) waitpid( id_second , nullptr , 0 ) ;
else if ( ! id_second ) /* child */
{
dup2( pipe_fd[ READ_END ] , STDIN_FILENO ) ;
close( pipe_fd[ READ_END ] ) ; //
replace_with( command1 ) ;
}
close( pipe_fd[ READ_END ] ) ;
}
}
int main () try
{
while ( true )
{
std::string command0 , command1 ;
getline( std::cin , command0 , '|' ) ;
getline( std::cin , command1 ) ;
pipeline( command0 , command1 ) ;
}
} catch ( const std::runtime_error& e )
{ std::cerr << e.what() ; return - 1 ; }
I'm actually doing my own shell.
I have done the following special characters:
int commande(int fin, int fout, char * com, char * param, int * bg){
// execute a command
(ex. ls –l)
int symbole;
char *mot;
pid_t pid;
symbole = parsing();
switch(symbole){
case 0: // NL
case 1: // ;
case 2: // &
case 3: // <
case 4: // >
case 5: // | (Here I have some issues when I try to redirect the output of a command).
(correspond à ctrl+D)
case 10:// Mot
default:
}
return;
}
But I have some issues to do the redirection of an output when it is piped " |", when I have two instructions that follow themselves. Indeed I have tried the following operations which have all worked:
>myShell ps > fich
>myShell ls -l | wc -l
But not this one:
>myShell ls -l | wc -l >file
here are the two cases specifically developped. I think that the issue is in the case 5 and not in the case 4 because the first command I tried worked (which I shew you above).
case 4: // SYMBOLE : >
if(output==0){
output=1;
execute=1;
for (l=0;l<10;l++){
eltsoutput[l]=eltsCommande[l];
}
}
break;
case 5: // SYMBOLE : |
//if(tube==0){
/*for (l=0;l<10;l++){
eltstube[l]=eltsCommande[l];
}*/
p2=fork();
if(p2==0){
if(tube==0){
freopen( "fichtmp", "w", stdout );
execvp(eltsCommande[0], eltsCommande);
}
return(0);
}
else{ if(background==0){ // SANS MOD BG ATTENDRE FIN FILS
waitpid(p2, NULL, 0);
}
tube=1;
execute=1;
}
break;
Can you help me finding a way to execute two commands at the same time with | and that allow their result to go to a file?
In my shell, the case one work in the case of a redirection with an instruction ";":
}else if(output==1){
close(1);
int filew = creat(eltsCommande[0], 0644);
execvp(eltsoutput[0], eltsoutput);
Maybe I should use this code to make it work?
Looking at the NetBSD /bin/sh source code, I see the following pipe implementation:
static int
sh_pipe(int fds[2])
{
int nfd;
if (pipe(fds))
return -1;
if (fds[0] < 3) {
nfd = fcntl(fds[0], F_DUPFD, 3);
if (nfd != -1) {
close(fds[0]);
fds[0] = nfd;
}
}
if (fds[1] < 3) {
nfd = fcntl(fds[1], F_DUPFD, 3);
if (nfd != -1) {
close(fds[1]);
fds[1] = nfd;
}
}
return 0;
}
This function is called by evalpipe with 2 file descriptors:
STATIC void
evalpipe(union node *n)
{
struct job *jp;
struct nodelist *lp;
int pipelen;
int prevfd;
int pip[2];
TRACE(("evalpipe(0x%lx) called\n", (long)n));
pipelen = 0;
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
pipelen++;
INTOFF;
jp = makejob(n, pipelen);
prevfd = -1;
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
prehash(lp->n);
pip[1] = -1;
if (lp->next) {
if (sh_pipe(pip) < 0) {
if (prevfd >= 0)
close(prevfd);
error("Pipe call failed");
}
}
if (forkshell(jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) {
INTON;
if (prevfd > 0) {
close(0);
copyfd(prevfd, 0, 1);
close(prevfd);
}
if (pip[1] >= 0) {
close(pip[0]);
if (pip[1] != 1) {
close(1);
copyfd(pip[1], 1, 1);
close(pip[1]);
}
}
evaltree(lp->n, EV_EXIT);
}
if (prevfd >= 0)
close(prevfd);
prevfd = pip[0];
close(pip[1]);
}
if (n->npipe.backgnd == 0) {
exitstatus = waitforjob(jp);
TRACE(("evalpipe: job done exit status %d\n", exitstatus));
}
INTON;
}
evalpipe is called in a switch statement in evaltree as follows:
case NPIPE:
evalpipe(n);
do_etest = !(flags & EV_TESTED);
break;
... which is called by the infinite loop in evalloop, and percolates up the tree till it gets to the eval function. I hope this helps.
Hii I have to make a code to read data from usb drive which can be pendrive and later a data acquisition card . i have written this much of code which detects all usb connection n print their info. Altough i don't know how to proceed further . I m also confused as to reading data from say pendrive means as in opening some files or what? Please also tell how to find endpoint of device currently i'm jsut using hit n trial to find it .
PLEASE help me out .I have read whole documentation on synchronous and asynchronous I/O.
enter code here
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
//=========================================================================
// This program detects usb and print out their details
//=========================================================================
int main (int argc, char *argv)
{
libusb_device **devList = NULL;
libusb_device *devPtr = NULL;
libusb_device_handle *devHandle = NULL;
libusb_context *ctx = NULL; //a libusb session
struct libusb_device_descriptor devDesc;
unsigned char strDesc[256];
ssize_t numUsbDevs = 0; // pre-initialized scalars
ssize_t idx = 0;
int retVal = 0;
//========================================================================
// test out the libusb functions
//========================================================================
printf ("*************************\n USB Detection Program:\n*************************\n");
retVal = libusb_init (&ctx);
if(retVal < 0) {
printf ("%d",retVal," Init Error "); //there was an error
return 1;
}
//========================================================================
// Get the list of USB devices visible to the system.
//========================================================================
numUsbDevs = libusb_get_device_list (ctx, &devList);
//========================================================================
// Loop through the list, looking for the device
//========================================================================
while (idx < numUsbDevs)
{
printf ("\n\n[%d]\n", idx+1);
//=====================================================================
// Get next device pointer out of the list, use it to open the device.
//=====================================================================
devPtr = devList[idx];
retVal = libusb_open (devPtr, &devHandle);
if (retVal != LIBUSB_SUCCESS)
break;
//=====================================================================
// Get the device descriptor for this device.
//=====================================================================
retVal = libusb_get_device_descriptor (devPtr, &devDesc);
if (retVal != LIBUSB_SUCCESS)
break;
//=====================================================================
// Get the string associated with iManufacturer index.
//=====================================================================
printf ("iManufacturer = %d", devDesc.iManufacturer);
if (devDesc.iManufacturer > 0)
{
retVal = libusb_get_string_descriptor_ascii
(devHandle, devDesc.iManufacturer, strDesc, 256);
if (retVal < 0)
break;
printf (" string = %s", strDesc);
}
//========================================================================
// Get string associated with iProduct index.
//========================================================================
printf (" \niProduct = %d", devDesc.iProduct);
if (devDesc.iProduct > 0)
{
retVal = libusb_get_string_descriptor_ascii
(devHandle, devDesc.iProduct, strDesc, 256);
if (retVal < 0)
break;
printf (" string = %s", strDesc);
}
//==================================================================
// Get string associated with iSerialNumber index.
//==================================================================
printf (" \niSerialNumber = %d", devDesc.iSerialNumber);
if (devDesc.iSerialNumber > 0)
{
retVal = libusb_get_string_descriptor_ascii
(devHandle, devDesc.iSerialNumber, strDesc, 256);
if (retVal < 0)
break;
printf (" string = %s", strDesc);
}
//==================================================================
// Print product id and Vendor id
//==================================================================
printf (" \nProductid = %d", devDesc.idProduct);
printf (" \nVendorid = %d", devDesc.idVendor);
//========================================================================
// Close and try next one.
//========================================================================
libusb_close (devHandle);
devHandle = NULL;
idx++;
//========================================================================
// Selection of device by user
//========================================================================
if(idx==numUsbDevs)
{ printf("\n\nselect the device : ");
scanf("%d",&idx);
if(idx > numUsbDevs)
{printf("Invalid input, Quitting.............");
break; }
devPtr = devList[idx-1];
retVal = libusb_open (devPtr, &devHandle);
if (retVal != LIBUSB_SUCCESS)
break;
retVal = libusb_get_device_descriptor (devPtr, &devDesc);
if (retVal != LIBUSB_SUCCESS)
break;
printf (" \nProductid = %d", devDesc.idProduct);
printf (" \nVendorid = %d", devDesc.idVendor);
unsigned char data[4] ; //data to read
//data[0]='a';data[1]='b';data[2]='c';data[3]='d'; //some dummy values
int r; //for return values
r = libusb_claim_interface(devHandle, 1); //claim interface 0 (the first) of device
if(r < 0) {
printf("\nCannot Claim Interface");
return 1;
}
printf("\nClaimed Interface");
int actual_length; //used to find out how many bytes were written
r = libusb_bulk_transfer(devHandle,LIBUSB_ENDPOINT_IN, data, 2, &actual_length, 0);
if (r == 0 && actual_length == sizeof(data)) {
// results of the transaction can now be found in the data buffer
// parse them here and report button press
}
else {
error();
}
r = libusb_release_interface(devHandle, 1); //release the claimed interface
if(r!=0) {
printf("\nCannot Release Interface");
return 1;
}
printf("\nReleased Interface");
idx=numUsbDevs +2;
}
} // end of while loop
if (devHandle != NULL)
{
//========================================================================
// Close device if left open due to break out of loop on error.
//========================================================================
libusb_close (devHandle);
}
libusb_exit (ctx); //close the session
printf ("\n*************************\n Done\n*************************\n");
return 0;
}
//==========================================
// EOF
//====================