Command Line Interfaces and Scripting
Staff Graded Assignment-2
By Sarthak Maji
Question 1:
#!/bin/bash
if [[ $# -eq 0 ]]; then
while read -r line; do
fruit=$(echo $line | awk '{print $1}')
price=$(echo $line | awk '{print $3}')
quantity=$(echo $line | awk '{print $4}')
total_price=$((price * quantity))
echo "$fruit $total_price"
done < fruits.txt
elif [[ $# -eq 1 ]]; then
if [[ $1 =~ ^[A-Za-z]$ ]]; then
grep " $1 " fruits.txt | awk '{total+=$3*$4; print $1;
fruit_prices+=$3*$4} END {print total}'
elif [[ $1 =~ ^[0-9]+$ ]]; then
awk -v limit=$1 '$3 <= limit {print $1}' fruits.txt
else
grep -i "^$1 " fruits.txt | awk '{print $2, $3}'
fi
else
echo "Error: Invalid number of arguments"
fi
Output:
2023EB03175 2023EB03175
Question 2
#!/bin/bash
TODO_FILE="todo.txt"
# Ensure the todo file exists
touch $TODO_FILE
# Function to display all tasks
display_tasks() {
if [ ! -s "$TODO_FILE" ]; then
echo "No tasks found."
return
fi
cat -n "$TODO_FILE"
}
# Function to display tasks based on status
display_tasks_by_status() {
local status=$1
local status_name
case "$status" in
0) status_name="TODO";;
1) status_name="DOING";;
2) status_name="COMPLETED";;
esac
if [ ! -s "$TODO_FILE" ]; then
echo "No tasks found."
return
fi
echo "$status_name tasks:"
grep " $status\$" "$TODO_FILE" | while read -r line; do
echo "$line"
done
}
# Function to add a new task
add_task() {
if [ -z "$1" ]; then
echo "Error: Task description required"
return 1
fi
local task="$1"
local serial_number=$(($(wc -l < "$TODO_FILE") + 1))
printf "%d %s %d\n" "$serial_number" "$task" 0 >>
"$TODO_FILE"
echo "Task added: $task"
}
# Function to mark tasks as complete
mark_complete() {
if [ $# -eq 0 ]; then
echo "Error: Please provide task number(s) to mark as
complete"
return 1
fi
for serial in "$@"; do
if ! [[ "$serial" =~ ^[0-9]+$ ]]; then
echo "Error: Invalid task number: $serial"
continue
fi
sed -i "${serial}s/ [012]$/ 2/" "$TODO_FILE"
done
echo "Marked tasks as complete: $@"
}
# Function to delete tasks
delete_tasks() {
for serial in "$@"; do
sed -i "${serial}d" $TODO_FILE
done
echo "Deleted tasks: $@"
}
# Main script logic
case "$1" in
"")
display_tasks_by_status 0
;;
"display")
display_tasks
;;
"add")
shift
add_task "$*"
;;
"todo")
display_tasks_by_status 0
;;
"doing")
display_tasks_by_status 1
;;
"complete")
display_tasks_by_status 2
;;
"mark_complete")
shift
mark_complete "$@"
;;
"delete")
shift
delete_tasks "$@"
;;
*)
echo "Error: Invalid command"
;;
esac
Output:
2023EB03175
2023EB03175
2023EB03175
2023EB03175
2023EB03175
2023EB03175
2023EB03175
Question 3:
#!/bin/bash
DATA_FILE="data.txt"
# Ensure the data file exists
touch "$DATA_FILE"
# Function to validate roll number (4 digits)
validate_roll() {
if [[ ! "$1" =~ ^[0-9]{4}$ ]]; then
return 1
fi
# Check if roll number already exists
if grep -q "^$1 " "$DATA_FILE"; then
return 1
fi
return 0
}
# Function to validate marks (0-100)
validate_marks() {
if [[ ! "$1" =~ ^[0-9]+$ ]] || [ "$1" -lt 0 ] || [ "$1" -gt
100 ]; then
return 1
fi
return 0
}
# Function to add student record
add_student() {
local roll name history geography civics
while true; do
read -p "Enter Roll Number (4 digits): " roll
if validate_roll "$roll"; then
break
fi
echo "Invalid roll number or roll number already exists.
Please try again."
done
read -p "Enter Name: " name
while true; do
read -p "Enter History Marks (0-100): " history
if validate_marks "$history"; then
break
fi
echo "Invalid marks. Please enter a number between 0 and
100."
done
while true; do
read -p "Enter Geography Marks (0-100): " geography
if validate_marks "$geography"; then
break
fi
echo "Invalid marks. Please enter a number between 0 and
100."
done
while true; do
read -p "Enter Civics Marks (0-100): " civics
if validate_marks "$civics"; then
break
fi
echo "Invalid marks. Please enter a number between 0 and
100."
done
echo "$roll $name $history $geography $civics" >>
"$DATA_FILE"
echo "Record added successfully!"
}
# Function to print passing students
print_passing_students() {
echo "List of Passing Students (>=33 in each subject):"
echo "Roll Name History Geography Civics"
echo "----------------------------------------"
while read -r line; do
read -r roll name history geography civics <<< "$line"
if [ "$history" -ge 33 ] && [ "$geography" -ge 33 ] && [
"$civics" -ge 33 ]; then
printf "%-6s %-6s %-8d %-9d %d\n" "$roll" "$name"
"$history" "$geography" "$civics"
fi
done < "$DATA_FILE"
}
# Function to calculate and print divisions
print_divisions() {
echo "Student Divisions:"
echo "Roll Name Average Division"
echo "--------------------------------"
while read -r line; do
read -r roll name history geography civics <<< "$line"
average=$(( (history + geography + civics) / 3 ))
if [ "$history" -lt 33 ] || [ "$geography" -lt 33 ] || [
"$civics" -lt 33 ]; then
division="Fail"
elif [ "$average" -ge 75 ]; then
division="First"
elif [ "$average" -ge 60 ]; then
division="Second"
else
division="Third"
fi
printf "%-6s %-6s %-9d %s\n" "$roll" "$name" "$average"
"$division"
done < "$DATA_FILE"
}
# Function to delete student record
delete_student() {
local roll
read -p "Enter Roll Number to delete: " roll
if grep -q "^$roll " "$DATA_FILE"; then
sed -i "/^$roll /d" "$DATA_FILE"
echo "Record deleted successfully!"
else
echo "Roll number not found!"
fi
}
# Main menu loop
while true; do
echo -e "\nStudent Record Management System"
echo "1. Add a student record"
echo "2. Print list of passing students"
echo "3. Print list of students with divisions"
echo "4. Delete a student record"
echo "5. Exit"
read -p "Enter your choice (1-5): " choice
case $choice in
1) add_student ;;
2) print_passing_students ;;
3) print_divisions ;;
4) delete_student ;;
5) echo "Goodbye!"; exit 0 ;;
*) echo "Invalid choice. Please enter a number between 1
and 5." ;;
esac
done
Output:
2023EB03175
2023EB03175
2023EB03175
Question 4
#!/bin/bash
EMAIL_FILE="emails.txt"
VALID_EMAIL_FILE="valid_emails.txt"
# Ensure the email file exists
touch "$EMAIL_FILE"
# Function to validate email format
validate_email() {
local email=$1
if [[ $email =~ ^[A-Za-z0-9]+@[A-Za-z]+\.com$ ]]; then
return 0
fi
return 1
}
# Function to add email
add_email() {
local email
read -p "Enter email address: " email
if validate_email "$email"; then
echo "$email" >> "$EMAIL_FILE"
echo "Email added successfully!"
else
echo "Invalid email format! Email should be in format:
letters/
[email protected]"
fi
}
# Function to get valid emails
get_valid_emails() {
echo "Checking for valid emails..."
# Clear the valid emails file
> "$VALID_EMAIL_FILE"
while IFS= read -r email || [ -n "$email" ]; do
if validate_email "$email"; then
echo "$email" >> "$VALID_EMAIL_FILE"
fi
done < "$EMAIL_FILE"
if [ -s "$VALID_EMAIL_FILE" ]; then
echo "Valid emails have been saved to $VALID_EMAIL_FILE"
echo "Valid emails:"
cat "$VALID_EMAIL_FILE"
else
echo "No valid emails found!"
fi
}
# Main menu loop
while true; do
echo -e "\nEmail Management System"
echo "1. Add email"
echo "2. Get valid emails"
echo "3. Exit"
read -p "Enter your choice (1-3): " choice
case $choice in
1) add_email ;;
2) get_valid_emails ;;
3) echo "Goodbye!"; exit 0 ;;
*) echo "Invalid choice. Please enter a number between 1
and 3." ;;
esac
done
Output:
2023EB03175
2023EB03175
Question 5
#!/bin/bash
SOURCE_DIR="source"
DEST_DIR="destination"
# Ensure both directories exist
if [ ! -d "$SOURCE_DIR" ] || [ ! -d "$DEST_DIR" ]; then
echo "Error: source and destination directories must exist"
exit 1
fi
# Function to compare files and print identical ones
compare_files() {
echo "Identical files in source and destination:"
local found=0
for src_file in "$SOURCE_DIR"/*; do
if [ ! -f "$src_file" ]; then continue; fi
filename=$(basename "$src_file")
dest_file="$DEST_DIR/$filename"
if [ -f "$dest_file" ] && cmp -s "$src_file"
"$dest_file"; then
echo "$filename"
found=1
fi
done
if [ $found -eq 0 ]; then
echo "No identical files found"
fi
}
# Function to copy non-identical files
copy_files() {
local extension="$1"
local copied=0
for src_file in "$SOURCE_DIR"/*; do
if [ ! -f "$src_file" ]; then continue; fi
filename=$(basename "$src_file")
dest_file="$DEST_DIR/$filename"
# If extension is specified, check if file matches
if [ -n "$extension" ]; then
if [[ "$filename" != *".$extension" ]]; then
continue
fi
fi
# Copy only if files are different or destination
doesn't exist
if [ ! -f "$dest_file" ] || ! cmp -s "$src_file"
"$dest_file"; then
cp "$src_file" "$dest_file"
echo "Copied: $filename"
copied=1
fi
done
if [ $copied -eq 0 ]; then
echo "No files copied"
fi
}
# Function to move all files
move_files() {
local moved=0
for src_file in "$SOURCE_DIR"/*; do
if [ ! -f "$src_file" ]; then continue; fi
filename=$(basename "$src_file")
mv "$src_file" "$DEST_DIR/"
echo "Moved: $filename"
moved=1
done
if [ $moved -eq 0 ]; then
echo "No files moved"
fi
}
# Function to perform backup operation
backup_files() {
echo "Performing backup..."
# Copy unique files from source to destination
for src_file in "$SOURCE_DIR"/*; do
if [ ! -f "$src_file" ]; then continue; fi
filename=$(basename "$src_file")
dest_file="$DEST_DIR/$filename"
if [ ! -f "$dest_file" ]; then
cp "$src_file" "$dest_file"
echo "Copied to destination: $filename"
elif ! cmp -s "$src_file" "$dest_file"; then
cp "$src_file" "$dest_file"
echo "Updated in destination: $filename"
fi
done
# Copy unique files from destination to source
for dest_file in "$DEST_DIR"/*; do
if [ ! -f "$dest_file" ]; then continue; fi
filename=$(basename "$dest_file")
src_file="$SOURCE_DIR/$filename"
if [ ! -f "$src_file" ]; then
cp "$dest_file" "$src_file"
echo "Copied to source: $filename"
fi
done
}
# Main script logic
case "$1" in
"")
compare_files
;;
"copy")
if [ -n "$2" ]; then
copy_files "$2"
else
copy_files
fi
;;
"move")
move_files
;;
"backup")
backup_files
;;
*)
echo "Invalid command"
echo "Usage:"
echo "./compare.sh - List identical files"
echo "./compare.sh copy - Copy non-identical files
from source to destination"
echo "./compare.sh copy ext - Copy files with specific
extension"
echo "./compare.sh move - Move all files from
source to destination"
echo "./compare.sh backup - Sync both directories"
;;
esac
Output:
2023EB03175
2023EB03175
Question 6:
#!/bin/bash
FILE="paragraph.txt"
# Check if file exists
if [ ! -f "$FILE" ]; then
echo "Error: $FILE does not exist"
exit 1
fi
# Function to count words
count_words() {
local count=$(wc -w < "$FILE")
echo "Number of words: $count"
}
# Function to count lines
count_lines() {
local count=$(wc -l < "$FILE")
echo "Number of lines: $count"
}
# Function to lookup word occurrences
lookup_word() {
local text="$1"
if [ -z "$text" ]; then
echo "Error: Please provide text to lookup"
return 1
fi
local count=$(grep -wo "$text" "$FILE" | wc -l)
echo "Occurrences of '$text': $count"
}
# Function to replace text
replace_text() {
local text1="$1"
local text2="$2"
if [ -z "$text1" ] || [ -z "$text2" ]; then
echo "Error: Please provide both text to replace and
replacement text"
return 1
fi
# Create backup of original file
cp "$FILE" "${FILE}.bak"
# Perform replacement
sed -i "s/\b${text1}\b/${text2}/g" "$FILE"
local count=$(grep -wo "$text2" "$FILE" | wc -l)
echo "Replaced '$text1' with '$text2'"
echo "Number of replacements: $count"
}
# Main script logic
case "$1" in
"words")
count_words
;;
"lines")
count_lines
;;
"lookup")
lookup_word "$2"
;;
"replace")
replace_text "$2" "$3"
;;
*)
echo "Usage:"
echo "./strings.sh words - Count
words"
echo "./strings.sh lines - Count
lines"
echo "./strings.sh lookup <text> - Count
occurrences of <text>"
echo "./strings.sh replace <text1> <text2> - Replace
<text1> with <text2>"
;;
esac
Output:
2023EB03175
Question7:
#!/bin/bash
INPUT_FILE="paragraph.txt"
BEGINNING_FILE="beginning.txt"
ENDING_FILE="ending.txt"
# Check if input file exists
if [ ! -f "$INPUT_FILE" ]; then
echo "Error: $INPUT_FILE does not exist"
exit 1
fi
# Clear or create output files
> "$BEGINNING_FILE"
> "$ENDING_FILE"
# Function to find words beginning with vowels
find_vowel_beginnings() {
echo "Finding words beginning with vowels..."
grep -oE '\b[aeiouAEIOU][a-zA-Z]*\b' "$INPUT_FILE" | sort -u
> "$BEGINNING_FILE"
echo "Words beginning with vowels saved to $BEGINNING_FILE"
}
# Function to find words ending with vowels
find_vowel_endings() {
echo "Finding words ending with vowels..."
grep -oE '\b[a-zA-Z]*[aeiouAEIOU]\b' "$INPUT_FILE" | sort -u
> "$ENDING_FILE"
echo "Words ending with vowels saved to $ENDING_FILE"
}
# Function to find words beginning and ending with same letter
find_same_letter_words() {
echo -e "\nWords beginning and ending with the same letter:"
echo "------------------------------------------------"
# Extract words and convert to lowercase for comparison
grep -oE '\b[a-zA-Z]+\b' "$INPUT_FILE" | while read -r word;
do
first_letter=$(echo "${word:0:1}" | tr '[:upper:]'
'[:lower:]')
last_letter=$(echo "${word: -1}" | tr '[:upper:]'
'[:lower:]')
if [ "$first_letter" = "$last_letter" ]; then
echo "$word"
fi
done
}
# Execute all functions
find_vowel_beginnings
find_vowel_endings
find_same_letter_words
# Print summary
echo -e "\nSummary:"
echo "Number of words beginning with vowels: $(wc -l <
"$BEGINNING_FILE")"
echo "Number of words ending with vowels: $(wc -l <
"$ENDING_FILE")"
Output:
2023EB03175
Question 8:
#!/bin/bash
# Function to check if directory exists
check_directory() {
if [ ! -d "$1" ]; then
echo "Error: Directory '$1' does not exist"
exit 1
fi
}
# Function to get file extension
get_extension() {
local filename="$1"
if [[ "$filename" == *.* ]]; then
echo "${filename##*.}"
fi
}
# Function to move file using a child process
move_file() {
local file="$1"
local ext="$2"
local dir_path="$3"
# Create target directory if it doesn't exist
local target_dir="${dir_path}/${ext}_files"
mkdir -p "$target_dir"
# Move file in a child process
(
if mv "$file" "$target_dir/"; then
echo "Moved: $(basename "$file") -> ${ext}_files/"
else
echo "Failed to move: $file"
fi
) &
}
# Main script
echo "Enter directory path:"
read -r dir_path
# Remove trailing slash if present
dir_path="${dir_path%/}"
# Validate directory
check_directory "$dir_path"
# Process files
echo "Processing files..."
for file in "$dir_path"/*; do
# Skip if not a file or is in a subdirectory
if [ ! -f "$file" ] || [ "$(dirname "$file")" != "$dir_path"
]; then
continue
fi
# Get file extension
ext=$(get_extension "$(basename "$file")")
if [ -n "$ext" ]; then
move_file "$file" "$ext" "$dir_path"
fi
done
# Wait for all child processes to complete
wait
echo -e "\nFile organization complete!"
echo "Summary of created directories:"
for dir in "$dir_path"/*_files/; do
if [ -d "$dir" ]; then
count=$(ls -1 "$dir" 2>/dev/null | wc -l)
dirname=$(basename "$dir")
echo "$dirname: $count files"
fi
done
Output:
2023EB03175
Question 9:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
// Signal handler for SIGCHLD
void handle_sigchld(int sig) {
int status;
pid_t pid;
// Wait for any child process that has terminated
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
printf("Child process %d has been cleaned up\n", pid);
}
}
// Function to demonstrate zombie process
void create_zombie() {
pid_t zombie_pid = fork();
if (zombie_pid < 0) {
perror("Fork failed");
exit(1);
}
if (zombie_pid == 0) {
// Child process
printf("Zombie child process %d is running\n",
getpid());
exit(0); // Child terminates immediately
} else {
// Parent process
printf("Parent process %d created potential zombie
%d\n", getpid(), zombie_pid);
sleep(2); // Give time to observe process states
}
}
// Function to demonstrate orphan process
void create_orphan() {
pid_t orphan_pid = fork();
if (orphan_pid < 0) {
perror("Fork failed");
exit(1);
}
if (orphan_pid == 0) {
// Child process that will become orphan
printf("Child process %d is running (Parent: %d)\n",
getpid(), getppid());
sleep(2); // Wait for parent to terminate
printf("Child process %d is now orphaned (New Parent:
%d)\n",
getpid(), getppid());
sleep(1);
exit(0);
} else {
// Parent process
printf("Parent process %d created potential orphan
%d\n",
getpid(), orphan_pid);
sleep(1); // Give child time to print initial message
printf("Parent process %d is terminating\n", getpid());
exit(0); // Parent terminates, making child an orphan
}
}
int main() {
// Set up signal handler for SIGCHLD
struct sigaction sa;
sa.sa_handler = handle_sigchld;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("\nDemonstrating Zombie Process:\n");
printf("-----------------------------\n");
create_zombie();
printf("\nDemonstrating Orphan Process:\n");
printf("-----------------------------\n");
create_orphan();
return 0;
}
Output:
2023EB03175
Question 10:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
// Global variables to control printing
volatile sig_atomic_t print_red = 1;
volatile sig_atomic_t print_yellow = 0;
volatile sig_atomic_t print_green = 0;
volatile sig_atomic_t should_exit = 0;
pid_t parent_pid;
// Signal handler for SIGUSR1 (from first child)
void handle_sigusr1(int signum) {
print_red = 0;
print_yellow = 1;
}
// Signal handler for SIGUSR2 (from second child)
void handle_sigusr2(int signum) {
print_yellow = 0;
print_green = 1;
should_exit = 1;
}
// Signal handler for SIGINT (Ctrl+C)
void handle_sigint(int signum) {
printf("\nParent process ID: %d\n", parent_pid);
exit(0);
}
int main() {
pid_t child1_pid, child2_pid;
parent_pid = getpid();
// Set up signal handlers
signal(SIGUSR1, handle_sigusr1);
signal(SIGUSR2, handle_sigusr2);
signal(SIGINT, handle_sigint);
// Create first child
child1_pid = fork();
if (child1_pid < 0) {
perror("Fork failed for child 1");
exit(1);
}
if (child1_pid == 0) {
// First child process
sleep(5);
kill(parent_pid, SIGUSR1);
exit(0);
}
// Parent continues to create second child
child2_pid = fork();
if (child2_pid < 0) {
perror("Fork failed for child 2");
exit(1);
}
if (child2_pid == 0) {
// Second child process
sleep(10);
kill(parent_pid, SIGUSR2);
exit(0);
}
// Parent process
printf("Parent PID: %d\n", parent_pid);
printf("Child 1 PID: %d\n", child1_pid);
printf("Child 2 PID: %d\n", child2_pid);
printf("\nStarting traffic light simulation...\n\n");
// Keep running until should_exit is set and green is shown
while (1) {
if (print_red) {
printf("\033[1;31mred\033[0m\n");
} else if (print_yellow) {
printf("\033[1;33myellow\033[0m\n");
} else if (print_green) {
printf("\033[1;32mgreen\033[0m\n");
if (should_exit) {
break;
}
}
usleep(500000); // Sleep for 0.5 seconds
}
printf("\nTraffic light simulation completed.\n");
return 0;
}
Output:
2023EB03175
2023EB03175
Note*: Easy Syntax Highlighter has been used to make the code look organized.