diff --git a/exercises/run_scripts/Makefile b/exercises/run_scripts/Makefile new file mode 100644 index 0000000..bd7090d --- /dev/null +++ b/exercises/run_scripts/Makefile @@ -0,0 +1,24 @@ +# Makefile for sequential script executor project + +CC = gcc +CFLAGS = -Wall -Wextra -std=c11 -g +TARGET = run_scripts +SRC = main.c + +.PHONY: all clean run test valgrind + +all: $(TARGET) + +$(TARGET): $(SRC) + $(CC) $(CFLAGS) -o $@ $^ + +run: $(TARGET) + ./$(TARGET) tests/hello.sh tests/fail.sh + +test: run + +valgrind: $(TARGET) + valgrind --leak-check=full ./$(TARGET) scripts/test1.sh scripts/test2.sh + +clean: + rm -f $(TARGET) diff --git a/exercises/run_scripts/README.md b/exercises/run_scripts/README.md new file mode 100755 index 0000000..36c613f --- /dev/null +++ b/exercises/run_scripts/README.md @@ -0,0 +1,73 @@ +# Sequential Script Executor + +This project contains a simple C program that sequentially executes one or more bash scripts using `fork()` and `execlp()`. + +## Features + +* Spawns a child process for each script +* Executes scripts using `/bin/bash` +* Waits for each child to finish before proceeding +* Reports exit status or signal termination + +## Usage + +### Build + +```bash +make +``` + +### Run with example scripts + +```bash +make run +``` + +### Run manually + +```bash +./run_scripts script1.sh script2.sh +``` + +### Run with Valgrind + +```bash +make valgrind +``` + +### Clean build + +```bash +make clean +``` + +## Project Structure + +``` +run_scripts/ # Root project folder +├── main.c # Main source code +├── Makefile # Build automation +├── README.md # Project documentation +└── tests/ # Example bash scripts for testing + ├── hello.sh + └── fail.sh +``` + +## Example Output + +```sh +execute script 1: test1.sh +script test1.sh exited with code 0 +execute script 2: test2.sh +script test2.sh exited with code 0 +``` + +## Notes + +* You can add as many scripts as needed in the command-line arguments. +* This tool can be used to batch test or automate shell scripts. + +## License + +MIT License + diff --git a/exercises/run_scripts/main.c b/exercises/run_scripts/main.c new file mode 100644 index 0000000..28db7da --- /dev/null +++ b/exercises/run_scripts/main.c @@ -0,0 +1,59 @@ +/* This program executes a sequence of Bash scripts + * in sequential order. Each script is run in a child + * process, and the parent prints the exit status. + */ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "usage: %s ...\n", argv[0]); + return EXIT_FAILURE; + } + + for (int i = 1; i < argc; i++) { + const char *script_name = argv[i]; + printf("execute script %d: %s\n", i, script_name); + + /* fork process for execute script itself */ + pid_t pid = fork(); + switch (pid) { + case -1: + perror("fork"); + exit(EXIT_FAILURE); + break; + case 0: + // child executes the current script + if (execlp("/bin/bash", "bash", script_name, + (char *)NULL) == -1) { + perror("execlp"); + exit(EXIT_FAILURE); + } + break; + default: + // parent waits for the child process + int status; + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + exit(EXIT_FAILURE); + } + if (WIFEXITED(status)) { + printf("script %s exited with code %d\n", + script_name, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + printf("script %s terminated by signal %d\n", + script_name, WTERMSIG(status)); + } else { + printf("script %s did not exit normally\n", + script_name); + } + break; + } + } + + return EXIT_SUCCESS; +} diff --git a/exercises/run_scripts/tests/fail.sh b/exercises/run_scripts/tests/fail.sh new file mode 100755 index 0000000..7b5799b --- /dev/null +++ b/exercises/run_scripts/tests/fail.sh @@ -0,0 +1,4 @@ +# fail.sh +#!/bin/bash +exit 1 + diff --git a/exercises/run_scripts/tests/hello.sh b/exercises/run_scripts/tests/hello.sh new file mode 100755 index 0000000..671aaf2 --- /dev/null +++ b/exercises/run_scripts/tests/hello.sh @@ -0,0 +1,5 @@ +# hello.sh +#!/bin/bash +echo "Hello, world!" +exit 0 +