Implementing a semaphore through a file - c

thank you for taking your time to read this.
I'm trying to implement a semaphore through a file using C on a linux machine.
I have two process that I must synchronize, one has all the consonants of a file stored in an array, the other has all the vowels.
I've arranged these arrays so that if I alternate between them, I can reconstruct the original file and paste it in another file.
The issue now is making these two process alternate.
This exercise in particular wants me to implement a semaphore using a 3rd file.
What I've done is use the first byte of this file as a semaphore, lot loop one process until the other is finished.
I've tested with long sleep()s and yes, if these processes do alternate, the output file is exactly how I want it, but with that said, my current implementation of a semaphore seems not to be working.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <fcntl.h>
int main (){
int child1=0, child2=0, fd, fd2, fs, i=0, i2=0;
int count=0, count2=0;
char buf1[20], buf2[20], a, b, con='1', vow='0', check1, check2;
fd=open("text", O_RDONLY);
fd2=open("text2", O_CREAT|O_RDWR|O_TRUNC,0777);
fs=open("semaphore", O_RDWR, 0777);
if (fork()==0)
child1=1;
else {
if (fork()==0)
child2=1;
}
//access vowel child
if (child1){
printf("I'm the first child\n");
while ((read(fd,&a,1))==1){
if (a=='a' || a=='e' || a=='i' || a=='o' || a=='u')
buf1[count]=a;
count++;
}
printf("count: %d\n", count);
//vowels are now into buf1
//wait for the brother to do the same
sleep(2);
for (i; i<=count+1; i++){
if (buf1[i]!='\0'){
printf("%c\n", buf1[i]);
a=buf1[i];
write(fd2,&a,1);
}
lseek(fs,0,SEEK_SET);
write(fs,&con,1);//tell the semaphore it's the consonant's turn (1)
while(check1=='1'){
lseek(fs,0,SEEK_SET);
read(fs,&a,1);
check1=a;
sleep(1);
} //get stuck until it's somebody's else turn
}
}
//access consonant child
else if (child2){
sleep(1);
lseek(fd,0,SEEK_SET);
printf ("I'm the second child\n");
while ((read(fd,&a,1))==1){
if (a=='a' || a=='e' || a=='i' || a=='o' || a=='u')
;
else
buf2[count2]=a;
count2++;
}
//resync
sleep(1);
printf("count: %d\n", count2);
//consonants are now into buf1
for (i; i<=count2+1; i++){
lseek(fs,0,SEEK_SET);
if (buf2[i]!='\0'){
printf("%c\n", buf2[i]);
b=buf2[i];
write(fd2,&b,1);
} //wait for vowel
while(check2=='0'){
lseek(fs,0,SEEK_SET);
read(fs,&b,1);
check2=b;
sleep(1);
}
lseek(fs,0,SEEK_SET);
write(fs,&vow,1);//tell the semaphore it's the vowel's turn (0)
}
}
else
printf("I'm the father\n");
sleep(10);
exit(0);
}
The file "text" has "hello world" stored in it. What happens when I execute this code is that what's copy and pasted is "hll wrld eoo". What exactly am I doing wrong with my semaphore?

Basically, you want to implement acquire/release similar to pthread_mutex_t or sem_t.
You need two functions:
acquire to wait for and acquire ownership
release to release ownership and grant it to the "other" process
You're conflating the two operations into one. And, your code doesn't actually do locking properly.
Side note: You are creating "zombie" processes because the father does not do wait on the children.
Here's some refactored code.
I added acquire and release calls to the places where you had your sempahore code (e.g. the second loops for each child). But, you might need them in the first loops as well (which I did not do).
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
void
acquire(int fs,char iam)
{
char own;
while (1) {
lseek(fs,0,SEEK_SET);
read(fs,&own,1);
if (own == iam)
break;
usleep(1);
}
}
void
release(int fs,char iam)
{
char other;
if (iam == 1)
other = 2;
else
other = 1;
lseek(fs,0,SEEK_SET);
write(fs,&other,1);
}
int
main()
{
int child1 = 0,
child2 = 0,
fd,
fd2,
fs,
i = 0,
i2 = 0;
int count = 0,
count2 = 0;
char buf1[20],
buf2[20],
a,
b,
con = '1',
vow = '0',
check1,
check2;
fd = open("text", O_RDONLY);
fd2 = open("text2", O_CREAT | O_RDWR | O_TRUNC, 0777);
fs = open("semaphore", O_RDWR | O_CREAT, 0777);
// initialize semaphore to grant access to one or the other
buf1[0] = 1;
write(fs,buf1,1);
if (fork() == 0)
child1 = 1;
else {
if (fork() == 0)
child2 = 1;
}
// access vowel child
if (child1) {
printf("I'm the first child\n");
while ((read(fd, &a, 1)) == 1) {
if (a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u')
buf1[count] = a;
count++;
}
printf("count: %d\n", count);
// vowels are now into buf1
// wait for the brother to do the same
sleep(2);
for (i; i <= count + 1; i++) {
acquire(fs,1);
if (buf1[i] != '\0') {
printf("%c\n", buf1[i]);
a = buf1[i];
write(fd2, &a, 1);
}
release(fs,1);
}
}
// access consonant child
else if (child2) {
sleep(1);
lseek(fd, 0, SEEK_SET);
printf("I'm the second child\n");
while ((read(fd, &a, 1)) == 1) {
if (a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u');
else
buf2[count2] = a;
count2++;
}
// resync
sleep(1);
printf("count: %d\n", count2);
// consonants are now into buf1
for (i; i <= count2 + 1; i++) {
acquire(fs,2);
if (buf2[i] != '\0') {
printf("%c\n", buf2[i]);
b = buf2[i];
write(fd2, &b, 1);
} // wait for vowel
release(fs,2);
}
}
else {
printf("I'm the father\n");
while (wait(NULL) >= 0);
printf("all done\n");
}
//sleep(10);
exit(0);
}
UPDATE:
The two child processes share a common file position for the semaphore file because the open call is done in the parent.
So, there is a potential race condition between the lseek and the read or write.
There are two ways to solve this:
Have each child open the semaphore file after the fork. Then, they do not share the file position.
Instead of lseek followed by read or write, use pread or pwrite.
Here is the latter:
void
acquire(int fs,char iam)
{
char own;
while (1) {
pread(fs,&own,1,0);
if (own == iam)
break;
usleep(1);
}
}
void
release(int fs,char iam)
{
char other;
if (iam == 1)
other = 2;
else
other = 1;
pwrite(fs,&other,1,0);
}

Related

Synchronizing two processes using POSIX named semaphore

I'm trying to write numbers (from 0-100 in incrementing order) on a file using two processes, where parent writes odd numbers and the child writes even numbers.
result on the file should look like this:
1 2 3 4 5 6 ...... 100
to do so i tried synchronizing using named semaphores
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>
FILE *ptr; //file pointer
sem_t *s_even, *s_odd; //declaring two semaphores
const char *even = "even", *odd = "odd"; //semaphore names
void fileWR(); // function containing fork call
void parent(); //parent which writes in file odd numbers only
void child(); //child which writes in file even numbers only
int main(void) {
ptr = fopen("Semaphore.txt", "w+"); // openning the file
fileWR();
return 0;
}
// ***fileWR()***
void fileWR() {
sem_unlink(even); //unlink any exsiting semaphores
sem_unlink(odd);
if (ptr == NULL) {
fprintf(stderr, "ERROR!");
exit(-1);
}
// i'm not handeling if fork failed
if (fork() != 0) { // fork() process
int status;
//Parent
//prints odd numbers only
puts("####Parent#################");
parent();
wait(&status); // wait for child to terminate
} else {
//child
//prints even numbers only
puts("####Child#################");
child();
}
}
// ***parent()***
void parent() {
int i = 0;
//openning semaphore initialized at 0 since count starts 0
s_odd = sem_open(odd, O_CREAT | O_EXCL, 0644, 0);
if (s_odd == SEM_FAILED) {
perror("Parent : [sem_open] Failed\n");
return;
}
while (i <= 100) {
if (i % 2 != 0) { //id i is odd
if (sem_wait(s_odd) != 0)
perror("sem_wait"); //wait for child to finish writing in file
//critical section
//writing in file
fprintf(ptr, "i=%d ", i);
printf("i=%d ", i); //visualize what's being written on file
if (sem_post(s_even) != 0)
perror("sem_post"); //allow child to write
}
i++; //move to the next number
}
//After printing all odd number and done with file and semaphore
sem_unlink(odd);
sem_close(s_odd);
fclose(ptr);
}
// ***child()***
void child() {
//semaphore initialized to 1 since since count starts with O
s_even = sem_open(even, O_CREAT | O_EXCL, 0644, 1);
if (s_even == SEM_FAILED) {
perror("Child : [sem_open] Failed\n");
return;
}
int j = 0;
while (j <= 100) {
if (j % 2 == 0) { //if current number is even
if (sem_wait(s_even) != 0)
perror("sem_wait"); //waiting for parent to finish writing
//critical section
//write in the file (excepcted to execute first because s_even = 1
fprintf(ptr, "j= %d ", j);
printf("j=%d ", j); // visualize what's being written in the file
if (sem_post(s_odd))
perror("sem_post"); // allow parent to write
}
j++; //move to the next number
}
//After printing all even number and done with file and semaphore
sem_unlink(even);
sem_close(s_even);
fclose(ptr);
}
This is my approach to solve the problem.
When I execute the program
####Parent#################
####Child#################
and nothing seems to happen after.
I'm supposing there's something wrong with semaphore implementation?
(Edit: I've updated my code and the segmentation fault was fixed)

The last thread Don't Run the task like other Threads in C

So in main function I have defined variable for creating variable.
Same thing for process.
Then I fork() the parent process and then inside the child process I have created 4 Threads and above the Main function ı have created the functions Where the Threads will run.
My question is The first 3 Thread works exactly as I want But the Fourth process not working as the First 3 Thread.
Here is my code that Child Process Execute.
I used Ordinary pipe to Write message from Parent Process and Read the Message from Child Process.
readBUFFER stands for the data I read from the Pipe and about 150 character length.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif
[const char readMessage\[152\];][1]
void *encrypt(char *data)
{
int shift = 4;
int i = 0;
for(i = 0; data[i] != '\0' ; ++i) {
char sm = data[i];
if(sm> 'a' && sm<='z'){
sm = sm + shift;
if(sm>'z'){
sm = sm - 'z' + 'a' - 1;
}
data[i] = sm;
}
else if(sm >= 'A' && sm<='Z'){
sm = sm + shift;
if(sm > 'Z'){
sm = sm - 'Z' + 'A'-1;
}
data[i] = sm;
}
}
return data;
}
void *threadFunc(void *message) {
printf("encrypted data : %s\n", encrypt(message));
return encrypt(message);
}
int main(void)
{
pthread_t threads[NUM_THREADS];
int pipefds[2];
//pipefds[0] - READ
//pipefds[1] - WRİTE
int returnstatus;
char readBUFFER[152];
int pid;
char writeMessages[152]={"150 characters is between 20 words and 40 words with spaces included in the character count. If spaces are not included in the character count,then 150 "};
returnstatus = pipe(pipefds);
if(returnstatus == -1)
{
printf("Pipe failed!\n");
return 1;
}
pid = fork(); //Child Process Oluşturma
if(pid == -1)
{
printf("Fork failed!\n");
return 2;
}
//Child Process
if(pid == 0)
{
close(pipefds[1]);
read(pipefds[0], readBUFFER , sizeof(readBUFFER));
printf("Child process Reads from pipe --> :%s\n",readBUFFER);
int buffer_size = strlen(readBUFFER);
printf("buffer size : %d\n",buffer_size);
char str[50];
for(int i = 1; i<=NUM_THREADS ;i++)
{
for(int j = ((buffer_size/4) * (i - 1)) ; j<=((buffer_size/4) * i) ; j++)
{
strncat(str , &readBUFFER[j] , 1);
}
printf("%d. Part --> %s\n", i,str);
pthread_create(&threads[i] , NULL , threadFunc , str);
sleep(2);
int pos = 0;
while(str[pos] != '\0')
{
str[pos] = '\0';
}
//pthread_join(threads[i] , NULL);
}
sleep(2);
}
//Parent process
else
{
close(pipefds[0]);
printf("parent process write to the pipe -->\n");
write(pipefds[1], writeMessages , sizeof(writeMessages));
wait(NULL);
printf("Child process Executed!");
}
return 0;
}
Basically encrypt function do the Caesar Encryption.
My question is when I run the First Thread they return the encrypted message but when it comes to the Fourth Thread it returns nothing.
I couldn't solve this problem maybe it's because ı haven't yet had mastered threads.
Let me know if you need more information.
and this is the output I took. Look at the 4 part. It represent the Fourth Thread.

Running concurrent processes using pipe in C

I'm working on an assignment in C aimed at using a pipe to pass variables between two processes. Both processes must be forked from the parent, and they must run concurrently to pass one character at a time (sort of demonstrated below).
The issue I'm having is that the fork()ed processes are not running concurrently. The sender seems to go first, and after running for ~26 seconds the receiver begins. Here is the code I have written:
#include <stdio.h>
int ret;
int pipearray[2];
char buffer[26];
void mysender();
void myreceiver();
int main()
{
int pid = 0;
int i = 0;
ret = pipe(pipearray);
while (i < 2) {
pid = fork();
if ( pid == 0 && i == 0 ) /* child process execution (receiver) */
{
myreceiver();
printf("Your receiver is done\n");
exit(0);
}
else if ( pid == 0 && i == 1 ) /* now executes sender */
{
mysender();
printf("Your sender is done\n");
exit(0);
}
++i;
}
close(pipearray[0]);
close(pipearray[1]);
sleep(30);
printf("Parent function has finished.\n");
return 0;
}
void mysender()
{
char c;
int index = 90;
close(pipearray[0]);
while (index > 64) /* loop for all values of A-Z in ASCII */
{
c = (char) index;
open(pipearray[1]);
write(pipearray[1], c, sizeof(c)); /* Sends letter to pipe */
--index;
sleep(1);
}
close(pipearray[1]);
}
void myreceiver()
{
int index = 0;
close(pipearray[1]);
while(buffer != 'A') /*loop runs until 'A' is handled */
{
sleep(1);
open(pipearray[0]);
read(pipearray[0], buffer, 1);
printf("%s", &buffer);
index++;
if ( index == 26 ) { break; }
}
close(pipearray[0]);
}
Expected Result:
ZYXWVUTSRQPONMLKJIHGFEDCBA
Your sender is done
Your receiver is done
The parent function has finished.
My result:
Your sender is done
The parent function has finished.
Your receiver is done
I'm very new to C programming but I've been banging away at this for a while. Any tips to why these might not be running simultaneously would be very appreciated.
There is many errors in your code. Don't try to open the pipe after fork, it is already open and has no name. Write should use the address of c. Read must read into the right place. A flush must be done after write to stdout. Your conditional must be slightly modified to be guaranteed correct. The parent process must wait its children. Here is the modified code :
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int ret;
int pipearray[2];
char buffer[26];
void mysender();
void myreceiver();
int main()
{
int pid = 0;
int i = 0;
ret = pipe(pipearray);
while (i < 2) {
pid = fork();
if ( pid == 0 && i == 0 ) /* child process execution (receiver) */
{
myreceiver();
printf("Your receiver is done\n");
exit(0);
}
else if ( pid == 0 && i == 1 ) /* now executes sender */
{
mysender();
printf("Your sender is done\n");
exit(0);
}
++i;
}
close(pipearray[0]);
close(pipearray[1]);
// Don't sleep, but wait until the end of the two children
wait(NULL);
wait(NULL);
// sleep(30);
printf("Parent function has finished.\n");
return 0;
}
void mysender()
{
char c;
int index = 90;
close(pipearray[0]);
while (index > 64) /* loop for all values of A-Z in ASCII */
{
c = (char) index;
// try to open a anonymous pipe is a non-sense
// open(pipearray[1]);
// Send a buffer by its address
write(pipearray[1], &c, sizeof(c)); /* Sends letter to pipe */
--index;
sleep(1);
}
close(pipearray[1]);
}
void myreceiver()
{
int index = 0;
close(pipearray[1]);
// Ensure condition is entered first
buffer[index] = 0;
// This is not the best condition ever, but ok.
while(buffer[index] != 'A') /*loop runs until 'A' is handled */
{
sleep(1);
// Don't open an anonymous pipe
// open(pipearray[0]);
// Read at the right position
read(pipearray[0], buffer+index, 1);
// print and flush, could also be printf("%s"...); flush(stdout);
printf("%s\n", buffer);
index++;
if ( index == 26 ) { break; }
}
close(pipearray[0]);
}
Now, consider to remove the sleeps in the reader, as it will be synchronized with the writes such that no read is possible if no write has been done. Alos consider to read more that one byte, because there is no concept of message, so that you can read as much byte you consider necessary to read, and as usual it is better to try reading a bunch of bytes when you can.

n-pipeline producing EOF on end

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
void tokenizer(char* input, char** output) { //My tokenizer
char* input_dup = strdup(input);
output[0] = strtok(input_dup, " ");
int i = 1;
while ((output[i] = strtok(NULL, " ")) != NULL) {
i++;
}
}
void run_command(char** args, int* fd) { //no pipe
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
close(fd[0]);
if (fd[1] != 1)
dup2(fd[1], 1);
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
wait(pid);
char buff[1];
while (read(fd[0], buff, 1) > 0) {
if (buff[0] == EOF || buff[0] == '\0') {
printf("Caught something, returning out...");
return;
}
else {
printf("%c", buff[0]);
}
}
}
}
//pipeline function
void run_pipe(char** args, int* fd) {
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
if (fd[1] != 1) {
dup2(fd[1], 1);
}
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
if (fd[0] != 0) {
dup2(fd[0], 0);
}
wait(pid);
}
}
int main(int argc, char** argv) {
printf ("Starting myshell (mysh) \n..\n..\n");
while (1) {
char cwd[1024];
printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));
char ch[1024];
memset(ch, 0, 1023); //for cleanup
char c = 0;
int i = 0;
while (c != '\n') {
c = getchar();
if (c == EOF) {
printf ("EOF Received, exiting...\n");
return 0;
}
if (c != '\n')
ch[i] = c;
i++;
}
if (ch[0] != '\0') {
char* tokens[128];
tokenizer(ch, tokens);
//first check for keywords
if (strcmp(tokens[0], "cd") == 0) {
if (chdir(tokens[1]) < 0) {
printf("ERROR: Directory %s does not exist\n", tokens[1]);
}
}
else if (strcmp(tokens[0], "exit") == 0) {
printf("Leaving shell...\n");
return 0;
}
else {
char* commands[50];
memset(commands, 0, sizeof(commands));
int j = 0;
int k = 0;
int fd[2];
//try something different...
while (tokens[j] != NULL) {
if (strcmp(tokens[j], "|") == 0) {
commands[k] = NULL;
pipe(fd);
run_pipe(commands, fd);
j++;
k = 0;
}
//more cases here
else { //nothing special
commands[k] = tokens[j];
j++;
k++;
}
}
commands[k] = NULL;
pipe(fd);
run_command(commands, fd);
}
}
}
}
The above code is meant to simulate a shell. It handles single commands and it handles the pipelining properly (i.e. ps | sort | wc is returning the correct output) however when the pipelining is done it returns an EOF which is caught by the condition in the loop with getchar(). If I try to ignore this EOF it segfaults. Am I leaving a pipe open somewhere and stdin is getting flooded? Any help is appreciated.
Compilation fixes
You need to add #include <sys/wait.h> and then fix the calls to wait(). I used (twice):
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
Arguably, that should be a loop looking for a specific PID, or you should use waitpid() instead. While debugging a shell, you want to know about every PID that exits and its status.
I ran `ps | wc' and got:
Starting myshell (mysh)
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
4 16 117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...
If you mean "the code should have continued instead of getting EOF", then there's some more work to do.
Tangential issues
I note the line:
if (buff[0] == EOF || buff[0] == '\0')
The character in buff[0] is from a read() call. It will never be EOF meaningfully; EOF is distinct from every character (hence getchar() returns an int). This becomes significant later:
char c = 0;
while (c != '\n')
{
c = getchar();
if (c == EOF)
Since c is a char, you cannot reliably compare it with EOF. You must store the result of getchar() in an int.
I'm not yet convinced these are the cause of the trouble, but you must be careful.
Probable cause
I think the trouble is in run_pipe() in the parent code (as amended):
else
{
close(fd[1]);
if (fd[0] != 0)
{
dup2(fd[0], 0);
}
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
}
The fd[0] != 0 condition will always be true (very improbable that it will be false), so you then change your shell's input to read from fd[0]. You should review that; it means that you're reading standard input from the read end of the pipe to the child. That's bad; you've lost your original input!
Your code also seems to have the parent waiting for the child to die, and then reads the pipe and echoes to standard output. This is not a good idea; it is better to make the child (last child in the pipeline) write to the standard output directly. There are two reasons for this:
The child might write more data than fits in a pipe, so it will block waiting for something to read its output, but the reader will be blocked waiting for the child to die, so you'll have a deadlock.
It slows things up, and output from the child may well be buffered instead of appearing timely on the terminal.
I'm a little sceptical about how a three-part pipeline would be handled. You need two pipes created before you run the middle process of the three; I don't see that in your code.
I was able to fix this issue. It's probably not the right way to do it, but I saved a copy of stdin and used dup2 to reset it when the pipelining was finished.
int in_bak = dup(0);
//stuff
dup2(in_bak, 0);
close(in_bak);

Using pipes to synchronize file-writing between processes

I have something that I've been staring at for most of the night and can't figure out. I'm writing code in C that is supposed to use pipes to pass a byte back and forth, allowing me to switch between a parent and child process that will take turns writing a string to a file. Here's my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2];
int fd2[2];
char token = 'a';
int file = open("output.txt", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
if (pipe(fd) == -1 || pipe(fd) == -1)
{
printf("Pipe failed");
return(-1);
}
pipe(fd2);
int pid = fork();
int i;
int j;
write(fd[1], token, 1);
if (pid) // Parent enters here
{
for (i = 0; i < 100;)
{
if (read(fd[0], token, 1) != -1)
{
write(file, "ppppppp", 7);
i++;
write(fd2[1], token, 1);
}
//usleep(500000);
}
wait();
}
else if (pid == 0) // Child enters here
{
for (j = 0; j < 100;)
{
if (read(fd2[0], token, 1) != -1)
{
write(file, "ccccc", 5);
j++;
write(fd[1], token, 1);
}
//usleep(500000);
}
}
else // Error creating child
{
exit (-1);
}
close(file);
return 0;
}
I know the writing to a file works when I don't use the pipes, but now I'm getting an infinite loop and I don't know what the problem is.
I figured it out! Funny how small things make all the difference.

Resources