-2

I would like to take an argument, that is given to me through the terminal and use it for zipping the files. For example, I have main.c file that is look something like that.

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

// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...

int main(int argc, char **argv)
{
    if(argc > 2){
        printf("Enough arguments\n");
        // tisk argumentu
        printf("%s\n\n", argv[1]);

        // tisk argumentů
        for (int i; i < argc; i++){
            printf("%s\n", argv[i]);
        }
    }
    else{
        printf("Insufficient number of arguments\n");
    } 
    return 0;
}

and I give it through the terminal these parameters (gzip text1.txt text2.txt) like this.

$ gcc main.c -o main
$ ./main gzip text1.txt text2.txt

My question is how to take the parameter gzip which is in argv[1] and apply this function to the text1.txt and text2.txt.

This is what I have now. Problem is, that execlp takes place only once and I wanted to make this possible for all arguments (text1.txt, text2.txt --> later for more than two) now I am able to make it possible only for one bcs I hard code it there (argv[2]) --> that is the text1.txt and I need to make it possible for n files. Is there someone who will be able to answer the question "For what I should replace the argv[2] to gzip all files instead only one?"

#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...

int main(int argc, char* argv[])
{
    
    if(argc > 2){
        printf("Enough arguments\n");
        // printing second argument
        printf("Program we use to compress: %s\n\n", argv[1]);

        // printing all arguments
        for (int i = 0; i < argc; i++){
            printf("%d.argument: %s\n", i, argv[i]);
        }

        int tmp;
        for (int i = 0; i < argc + 1; i++){
            if (i < 2){   
                tmp ++;
            }
            else{
                fork();
                 // execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
                execlp(
                argv[1],
                argv[1],
                argv[2],
                NULL
                );
                // if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
                int err = errno;
                if (err == 2){
                    printf("File with that name not found or path to that file was wrong!\n");
                    break;
                }
            }
        }
    }
    else{
        printf("Insufficient number of arguments\n");
    } 
    return 0;
}

po úpravě

#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...

int main(int argc, char* argv[])
{
    
    if(argc > 2){
        printf("Enough arguments\n");
        // printing second argument
        printf("Program we use to compress: %s\n\n", argv[1]);

        // printing all arguments
        for (int i = 0; i < argc; i++){
            printf("%d.argument: %s\n", i, argv[i]);
        }

        for (int i = 2; i < argc + 1; i++){
            // execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
            execlp(
            argv[1],
            argv[1],
            argv[i],
            NULL
            );
            // if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
            int err = errno;
            if (err == 2){
                printf("File with that name not found or path to that file was wrong!\n");
                break;
            }
        }
    }
    else{
        printf("Insufficient number of arguments\n");
    } 
    return 0;
}
  • 7
    basically two options: [`system()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/system.html) or [[`fork()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)] / [`exec*()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html) (or equivalent on Windows: https://stackoverflow.com/a/985288/25324) – pmg May 05 '22 at 12:36
  • 4
    Side note: `for (int i; i < argc; i++){` You should initialize `int i = 0` here. Or `int i = 1`... – 001 May 05 '22 at 12:39
  • 4
    "how to take the parameter gzip which is in argv[1] and apply this function" What does this mean? Do you want to **run the existing program** named `gzip`? Did you **write a function in your own code** named `gzip`, and want to choose that according to the text that was typed? (If so, why not show such a function in the example?) Something else? – Karl Knechtel May 05 '22 at 12:40
  • Yes, I would like to use fork() because I aim to make individual process for each zipping file, but I have never done this before, so I am a bit confused by the forking. – Michael Hajný May 05 '22 at 12:40
  • Build with `gcc -Wall -Wextra -Werror main.c -o main`, fix errors. – hyde May 05 '22 at 12:45
  • 3
    The normal term for what you are calling a “function” is “command” or “program”. Function implies something that is part of the current program — part of the code you wrote. Command implies something that is separate which you may or may not have written. – Jonathan Leffler May 05 '22 at 12:46
  • My intention was to create a program for the Linux operating system that would simultaneously compress the specified files and should accept at least two arguments, the first being the name of the compression program (eg. gzip, bzip2) that will provide the compression, and then the list of files to be compressed . --> I thought that if I pass as a first argument the gzip than I am able to take it from argv[1] and apply to the text1.txt ... text.n.txt. That is what I asked for. – Michael Hajný May 05 '22 at 12:51
  • @pmg Doesn't Linux support _any_ other way of launching a number of processes without either using the problematic system() call or running some useless overhead copy code involved when forking? Either option sounds like Unix stone age API... Or can you call exec without replacing the current process? – Lundin May 05 '22 at 13:22
  • Why don't you just use a loop in the shell? – the busybee May 05 '22 at 13:23
  • @Lundin I'm not a Linux expert (not by a loooong long shot!) but I understand those are the only 2 options: `system or popen` or `fork-exec`. However, `fork()` (again, my understanding) is very streamlined and doesn't really copy anything to the new process, unless it needs to (unless you don't `exec*()` promptly). – pmg May 05 '22 at 14:22
  • Both `system` and `popen` are essentially parent setup + `fork` + child setup + `exec` under the hood. And `fork` is fairly light weight. If you need more fine-grained control, there's Linux system call https://man7.org/linux/man-pages/man2/clone.2.html – hyde May 05 '22 at 15:10

2 Answers2

0

The execv function takes an array of char *, the last of which must be NULL. The argv array fits this definition, so you can pass in the address of argv[1] which gives you the command specified and its parameters.

Also, you should check the return value of fork and only call execv in the child process.

if (fork() == 0) {
    // child
    execv(argv[1], &argv[1]);
    perror("exec failed");
    _exit(1);
} else {
    // parent
    wait(NULL);
}
dbush
  • 205,898
  • 23
  • 218
  • 273
0

There it is! Finall working solution

#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...

int main(int argc, char* argv[])
{
    
    if (argc > 2){
        printf("Enough arguments\n");
        // printing second argument
        printf("Program we use to compress: %s\n\n", argv[1]);

        // printing all arguments
        printf("Arguments we use:\n");
        for (int i = 0; i < argc; i++){
            printf("%d.argument: %s\n", i, argv[i]);
        }

        // we start our loop from third argument
        for (int i = 2; i < argc + 1; i++){
            
            // splitting our program to n processes
            int id = fork();
            
            if (id == 0){
                // execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
                execlp(
                argv[1], // program we use
                argv[1], // program we use
                argv[i], // the document to which the program is applied
                NULL
                );
                // if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
                int err = errno;
                if (err == 2){
                    printf("File with that name not found or path to that file was wrong!\n");
                    break;
                }
            }
        }
    }
    else{
        printf("Insufficient number of arguments\n");
    } 
    return 0;
}