Linux.org User-created Helpful Shell Scripts

C

CrazedNerd

Guest
Sometimes google being good at predicting what you want to find is kind of boring and un-adventurous, this script randomizes a set of options based on time, and makes it a little less predictable by putting the potential websites you have in the slot in reverse order based on whether your last system clock digit is even or odd...it's practical if you want to look at familiar/regularly-updated websites from a different perspective...just use this to get a website without getting useless error messages from firefox:

Code:
firefox <URL> &> /dev/null/ &

...in place of "echo slot _____"

A special thanks to @sphen, @dos2unix, and @KGIII for helping me figure this out:

Code:
#!/bin/bash
#if your total outcomes are less that 10, then subtract from digit:
#for example: modified=$(d-1) if you only want to display 9 options

now=$(date +%s)
eo=$((now%2))
d=$(echo $now | cut -c 10)

if [ "$eo" = 1 ]; then
    case $d in
            0)
                echo "Slot 1"
                exit 0
                ;;
            1)
                echo "Slot 2"
                exit 0
                ;;
            2)
                echo "Slot 3"
                exit 0
                ;;
            3)
                echo "Slot 4"
                exit 0
                ;;        
            4)
                echo "Slot 5"
                exit 0
                ;;
            5)
                echo "Slot 6"
                exit 0
                ;;
            6)
                echo "Slot 7"
                exit 0
                ;;
            7)
                echo "Slot 8"
                exit 0
                ;;
            8)
                echo "Slot 9"
                exit 0
                ;;
            *)  echo "Slot 1"0
                exit 0
                ;;
        esac
else
    case $d in
            0)
                echo "Slot 9"
                exit 0
                ;;
            1)
                echo "Slot 8"
                exit 0
                ;;
            2)
                echo "Slot 7"
                exit 0
                ;;
            3)
                echo "Slot 6"
                exit 0
                ;;        
            4)
                echo "Slot 5"
                exit 0
                ;;
            5)
                echo "Slot 4"
                exit 0
                ;;
            6)
                echo "Slot 3"
                exit 0
                ;;
            7)
                echo "Slot 2"
                exit 0
                ;;
            8)
                echo "Slot 1"
                exit 0
                ;;
            *)  echo "Slot 0"
                exit 0
                ;;
        esac
fi

In hundreds of years, you will need to change it to "cut -c 11", because it will have been 10's of billions of seconds since the dawn of unix instead of a mere billions of seconds...

EDIT:

Oops! Above is logically wrong....

ACTUALLY, the even odd thing doesn't make any sense because you make it impossible to hit 9 and 7....it's better to just use a factor of 10 for ten options:

Code:
#!/bin/bash

now=$(date +%s)
d=$(echo $now | cut -c 10)

case $d in
    0)
        echo "Slot 1"
        exit 0
        ;;
    1)
        echo "Slot 2"
        exit 0
        ;;
    2)
        echo "Slot 3"
        exit 0
        ;;
    3)
        echo "Slot 4"
        exit 0
        ;;         
    4)
        echo "Slot 5"
        exit 0
        ;;
    5)
        echo "Slot 6"
        exit 0
        ;;
    6)
        echo "Slot 7"
        exit 0
        ;;
    7)
        echo "Slot 8"
        exit 0
        ;;
    8)
        echo "Slot 9"
        exit 0
        ;;
    *)  echo "Slot 10"
        exit 0
        ;;
esac

You could also use combinations of the last number of seconds to produce more possibilities (adding/multiplying/dividing the last two numbers), you would just keep adding more tests for possibilities...i just think that practical and fun and basically the same...
 
Last edited by a moderator:


MattWinter

Active Member
Joined
Dec 5, 2022
Messages
178
Reaction score
223
Credits
1,308
This is a good script for beginner c/c++ programmers. If you put your c executables inside of your home folder, you'd be just fine making this an alias:

Code:
#for compiling/testing/degbugging code in c with one command

gcc $1; a.out

I like the fact that gcc has the "a.out" convention, because it allows to test your code quickly before naming it.
Most C programmers use makefiles. If you're learning C, they would be a hood thing to get familiar with.
 

sphen

Well-Known Member
Joined
Dec 12, 2022
Messages
871
Reaction score
753
Credits
10,429
EDIT:

Oops! Above is logically wrong....

ACTUALLY, the even odd thing doesn't make any sense because you make it impossible to hit 9 and 7....it's better to just use a factor of 10 for ten options: [See second code example in post 101, above.]
That was a good use of the case statement, but the code can be tightened. Here is a functional equivalent:

Code:
#!/bin/bash

now=$(date +%s)
d=$(echo $now | cut -c 10)
echo "Slot $((d+1))"   # Replaces case statement in previous version
exit 0
 
C

CrazedNerd

Guest
That was a good use of the case statement, but the code can be tightened. Here is a functional equivalent:

Code:
#!/bin/bash

now=$(date +%s)
d=$(echo $now | cut -c 10)
echo "Slot $((d+1))"   # Replaces case statement in previous version
exit 0
the problem with the odd even scheme though is that if you do get 9, 7, or 5, the reversal in the second case statement (for odd numbers) just changes which slot it is, making it so that you can't even get those numbers. It's ultimately just a logical problem that doesn't have a soltion, i somehow thought using my coin flip idea would make it more random but since it's using the same system clock setting it doesn't work. You have to use different algorithmic or mathematical schemes if you want to simulate randomness, because the selection of 10 is as far as it goes. I definitely over thought that problem!
 

sphen

Well-Known Member
Joined
Dec 12, 2022
Messages
871
Reaction score
753
Credits
10,429
Okay, one last time for that script above. The original version relied on the Unix clock that changed once a second. It gave predictable results, especially if you ran it multiple times in a row.

Here is my final version of the same script. The results will not be predictable if you run it over and over, because it relies on the nanosecond clock. Tested on Debian, Mint, and Oracle Linux.
Caution: Fails on macOS. The date command nanosecond option (%N) is not supported.

Code:
#!/bin/bash
now=$(date +%N)  # Get the nanoseconds from the time. Ignore seconds, minutes...
# echo "nanoseconds = $now"  # Uncomment this line to see the nanoseconds
d=$(echo $now | cut -c 9)
echo "Slot $((d+1))"
exit 0

I hope this helps.
 
C

CrazedNerd

Guest
Okay, one last time for that script above. The original version relied on the Unix clock that changed once a second. It gave predictable results, especially if you ran it multiple times in a row.

Here is my final version of the same script. The results will not be predictable if you run it over and over, because it relies on the nanosecond clock. Tested on Debian, Mint, and Oracle Linux.
Caution: Fails on macOS. The date command nanosecond option (%N) is not supported.

Code:
#!/bin/bash
now=$(date +%N)  # Get the nanoseconds from the time. Ignore seconds, minutes...
# echo "nanoseconds = $now"  # Uncomment this line to see the nanoseconds
d=$(echo $now | cut -c 9)
echo "Slot $((d+1))"
exit 0

I hope this helps.
Wow, i had no idea that existed, thanks.
 
C

CrazedNerd

Guest
Here's a script for categorizing/analyzing the words listed in a dictionary file for novice cyptographers.

Each word has to be listed on each line. This one is pretty in depth, but of course you can do without knowing which words start with which letters, you might want to replace that with or add better information about how long the words are:
Code:
#!/bin/bash
#used to categorize words in any dictionary

#/usr/share/dict/words
echo "Enter dictionary name, or absolute path if not"
read -p "In working directory: " file

if ! [ -f $file ]; then
    echo "Invalid file, try again."
fi

total=$(cat $file | wc -l)

echo -e "\n$file contains $total words."

large=$(sed '/[A-Za-z0-9]\{8\}.*/!d' $file | wc -l)

echo -e "\nThis file contains $large words that are 8 letters or longer."

small=$(sed '/[A-Za-z0-9]\{8\}/d' $file | wc -l)

echo -e "\nThis file contains $small words that are smaller than 8 letters.\n"

start_a=$(sed '/^[Aa]/!d' $file | wc -l)

echo -e "This file contains $start_a words that start with a"

start_b=$(sed '/^[Bb]/!d' $file | wc -l)

echo -e "This file contains $start_b words that start with b"

start_c=$(sed '/^[Cc]/!d' $file | wc -l)

echo -e "This file contains $start_c words that start with c"

start_d=$(sed '/^[Dd]/!d' $file | wc -l)

echo -e "This file contains $start_d words that start with d"

start_e=$(sed '/^[Ee]/!d' $file | wc -l)

echo -e "This file contains $start_e words that start with e"

start_f=$(sed '/^[Ff]/!d' $file | wc -l)

echo -e "This file contains $start_f words that start with f"

start_g=$(sed '/^[Gg]/!d' $file | wc -l)

echo -e "This file contains $start_g words that start with g"

start_h=$(sed '/^[Hh]/!d' $file | wc -l)

echo -e "This file contains $start_h words that start with h"

start_i=$(sed '/^[Ii]/!d' $file | wc -l)

echo -e "This file contains $start_i words that start with i"

start_j=$(sed '/^[Jj]/!d' $file | wc -l)

echo -e "This file contains $start_j words that start with j"

start_k=$(sed '/^[Kk]/!d' $file | wc -l)

echo -e "This file contains $start_k words that start with k"

start_l=$(sed '/^[Ll]/!d' $file | wc -l)

echo -e "This file contains $start_l words that start with l"

start_m=$(sed '/^[Mm]/!d' $file | wc -l)

echo -e "This file contains $start_m words that start with m"

start_n=$(sed '/^[Nn]/!d' $file | wc -l)

echo -e "This file contains $start_n words that start with n"

start_o=$(sed '/^[Oo]/!d' $file | wc -l)

echo -e "This file contains $start_o words that start with o"

start_p=$(sed '/^[Pp]/!d' $file | wc -l)

echo -e "This file contains $start_p words that start with p"

start_q=$(sed '/^[Qq]/!d' $file | wc -l)

echo -e "This file contains $start_q words that start with q"

start_r=$(sed '/^[Rr]/!d' $file | wc -l)

echo -e "This file contains $start_r words that start with r"

start_s=$(sed '/^[Ss]/!d' $file | wc -l)

echo -e "This file contains $start_s words that start with s"

start_t=$(sed '/^[Tt]/!d' $file | wc -l)

echo -e "This file contains $start_t words that start with t"

start_u=$(sed '/^[Uu]/!d' $file | wc -l)

echo -e "This file contains $start_u words that start with u"

start_v=$(sed '/^[Vv]/!d' $file | wc -l)

echo -e "This file contains $start_v words that start with v"

start_w=$(sed '/^[Ww]/!d' $file | wc -l)

echo -e "This file contains $start_w words that start with w"

start_x=$(sed '/^[Xx]/!d' $file | wc -l)

echo -e "This file contains $start_x words that start with x"

start_y=$(sed '/^[Yy]/!d' $file | wc -l)

echo -e "This file contains $start_y words that start with y"

start_z=$(sed '/^[Zz]/!d' $file | wc -l)

echo -e "This file contains $start_y words that start with y"

start_num=$(sed '/^[0-9]/!d' $file | wc -l)

echo -e "This file contains $start_num words that start with a number"
 
C

CrazedNerd

Guest
Here's a nice little trick I discovered today...in so many scripts, you are going to find it most convenient to pass it files as arguments, and the error messages you put at the beginning of your scripts should tell you if you forget to do this.

I normally don't use functions in bash scripts because i find a lot of situations to be different, and functions are called just to save redundancy...however, i've found that scripts often just have ONE argument. So, you can put this in your .bashrc folder to save you typing in future scripts:

Code:
noArgs()
{

if [ $# = 0 ]; then
  echo "Please enter an argument for script. Thank you."
  exit 0
fi

}

export -f noArgs

Pass the argument to the the function in the script like this:

Code:
noArgs $1

and that would call the function if you use your script incorrectly...
 
Last edited by a moderator:

JasKinasis

Well-Known Member
Joined
Apr 25, 2017
Messages
1,820
Reaction score
2,656
Credits
15,608
Here's something I made given the default lack of a timer on Ubuntu, would be even more practical if I could turn this into a clickable desktop shortcut:

Code:
#! /bin/bash
 
 firefox https://www.timeanddate.com/timer/ 2>/dev/null

the redirection at the end is to free up the terminal for other uses and get rid of the worthless error message. Given the auto-complete of firefox (after a couple of visits, entering the URL would only mean entering "t"), you can still win interms of efficiency if you know how to make this into a clickable shortcut!

EDIT: removed line numbers from script to keep any coding novice from getting confused.
One thing you could do here, is find Firefox.desktop (I think it’s usually somewhere like /usr/share/applications/), copy it to ~/.local/share/applications and rename it to something like timer.desktop.
E.g.
Bash:
cp /usr/share/applications/firefox.desktop ~/.local/share/applications/online-timer.desktop
Note: firefox.desktop might be called Firefox.desktop. I’m not near a Linux machine right now. Check the casing before running the above command!

Then open online-timer.desktop with a text editor and edit the Exec field (which runs Firefox) and add the url to the dateandtime website to the end.

That way it will run Firefox and open the url to the timer website. No need for a script.

Putting the desktop shortcut in ~/.local/share/applications should immediately make the shortcut available in any launchers/system menus. If you’re using an old school hierarchical menu, with applications in categories, it will appear in the same section of the menu as firefox.
If you want it to appear in a different section - e.g. Utilities - change the Categories field of the file, to put it in Utilities instead of in the Internet/Web section. (I can’t remember the names of all of the categories offhand, but the default category for firefox will be something like Internet, or Web, or something!
You can even give it a different icon, by editing the Icon field in the .desktop file.

Once you’ve made the edits, you can also, optionally add a copy of the .desktop file to your desktop. Either by copying it there using cp, or by creating a symbolic link (equivalent to a shortcut on windows).
e.g.
Bash:
ln -s -T ~/.local/share/applications/online-timer.desktop ~/Desktop/online-timer
Above creates a symbolic link on your desktop called online-timer, which is linked to online-timer.desktop.
Double clicking on the shortcut on your desktop will run your modified .desktop file, which will run Firefox and load the timer web-page.

Using both of these methods, you can launch the timer web-page from your system menus, or other GUI launchers. And you’ll also have a shortcut directly on the desktop.

And this will work with any scripts/programs you create too.
You can add your scripts to GUI menus/launchers by creating .desktop files for them.

I have a recent-ish post which briefly explains how to create .desktop files for scripts/applications here. Most of the post is about adding a personal bin directory to put your scripts in. The end of the post tells you how to add a .desktop launcher.

There is another, older thread here, where I went into more detail about .desktop files and provided a link to freedesktop.org’s specification for .desktop files.
 
OP
K

KGIII

Super Moderator
Staff member
Gold Supporter
Joined
Jul 23, 2020
Messages
11,211
Reaction score
9,723
Credits
93,025
C

CrazedNerd

Guest
Here is a script for backing up your entire home directory very easily and with almost no
need for thought or caution. Unfortunately it takes a long time if you have a "snap" folder
or files from other software installed in home...plus USBs are still pretty slow.

It's different from some of the other backup scripts i've seen as it's open ended and explicity for people who
use flash drives:

Code:
#!/bin/bash

df -h

read -p "Enter destination drive name (not device name). Enter full path: " dest

while ! [ -d $dest ]; do
    read -p "That is not a valid directory, try again or press Ctrl-C to exit: " dest
done

today=$(date | awk '{print $2, $3, $7}' | sed 's/ /-/g')

mkdir "$dest/BackupFolder$today"

cp -v ~/.* "$dest/BackupFolder$today"

cp -rv ~/* "$dest/BackupFolder$today"
 
C

CrazedNerd

Guest
Then open online-timer.desktop with a text editor and edit the Exec field (which runs Firefox) and add the url to the dateandtime website to the end.
I'm not fallowing you here about the exec field, do you mean append the URL to the path?

Just for your info, firefox.desktop is not located in usr/share/applications/ on my system, but here:

Code:
/snap/firefox/1635/firefox.desktop
/snap/firefox/1635/meta/gui/firefox.desktop
/var/lib/snapd/desktop/applications/firefox_firefox.desktop
 
C

CrazedNerd

Guest
Most C programmers use makefiles. If you're learning C, they would be a hood thing to get familiar with.
I researched that a little bit, is "make" just used for compiling and installation, or can it be used to run the binaries too?

Anyways, during formatting, i accidentally erased all my c stuff (it's better to put everything in your home directory to make backups simpler and more efficient), and as a response decided to make C file creation really easy on myself:

Code:
#!/bin/bash
noArgs $1 #see .bashrc hack above

echo "#include <stdio.h>" >> $1

echo "#include <stdlib.h>" >> $1

echo "int main ()" >> $1

echo -e "{\n}" >> $1

nano $1
 

JasKinasis

Well-Known Member
Joined
Apr 25, 2017
Messages
1,820
Reaction score
2,656
Credits
15,608
I'm not fallowing you here about the exec field, do you mean append the URL to the path?

Just for your info, firefox.desktop is not located in usr/share/applications/ on my system, but here:

Code:
/snap/firefox/1635/firefox.desktop
/snap/firefox/1635/meta/gui/firefox.desktop
/var/lib/snapd/desktop/applications/firefox_firefox.desktop
I mean in your copy of the Firefox.desktop file, edit the Exec=/path/to/firefox field to amend the website address.
E.g.
Code:
Exec=/path/to/firefox https://www.timeanddate.com/timer/

However, your Firefox is using snapd, ugh! So I don’t know offhand if this approach will work or not. You could give it a go though. It should work.

And if it doesn’t, you can add yet another item to the list of reasons why snapcraft/snapd completely sucks!
 
C

CrazedNerd

Guest
I mean in your copy of the Firefox.desktop file, edit the Exec=/path/to/firefox field to amend the website address.
E.g.
Code:
Exec=/path/to/firefox https://www.timeanddate.com/timer/

However, your Firefox is using snapd, ugh! So I don’t know offhand if this approach will work or not. You could give it a go though. It should work.

And if it doesn’t, you can add yet another item to the list of reasons why snapcraft/snapd completely sucks!
I like the timer script i posted earlier a lot better than that site, but i will try to test that out with a different site, thank you
 

sphen

Well-Known Member
Joined
Dec 12, 2022
Messages
871
Reaction score
753
Credits
10,429
I researched that a little bit, is "make" just used for compiling and installation, or can it be used to run the binaries too?

Anyways, during formatting, i accidentally erased all my c stuff (it's better to put everything in your home directory to make backups simpler and more efficient), and as a response decided to make C file creation really easy on myself:

Code:
#!/bin/bash
noArgs $1 #see .bashrc hack above
echo "#include <stdio.h>" >> $1
echo "#include <stdlib.h>" >> $1
echo "int main ()" >> $1
echo -e "{\n}" >> $1
nano $1
The make utility is a powerful tool for building software. If you are writing C code, it is very useful to know and understand make, makefiles, and how they work together.

Make understands the dependencies between the files in your program. If you modify a file, make will automatically determine what other files are affected and compiles and links them accordingly. This gets more and more useful as your programs increase in complexity.

Yes, you can use it to run binaries. It is common to use make to build and run automated test programs that verify you did not make a modification that "broke" the mainline code. Automated testing may be included in the build process.

For writing small C programs, you do not need a complex makefile. Take a very basic makefile and modify it to fit your projects. My feeling is that you can learn the basics of makefiles from online searches.

(BOOK ABOUT make - NOT RECOMMENDED FOR BEGINNERS: I have an old book with too much detail that I do NOT recommend for beginners: "Managing Projects with make" from O'Reilly Press. I believe that the best way for @CrazedNerd to learn enough basics of make and makefiles is through online searches, tutorials, and/or examples.)
 

sphen

Well-Known Member
Joined
Dec 12, 2022
Messages
871
Reaction score
753
Credits
10,429
RECOMMENDATION: A Reference Book for C Programmers

I have written a few lines of C code and would like to share a helpful book. I like to have it nearby when programming in C. I bought two copies - one at work and one at home (or sometimes in my laptop case).

C: A Reference Manual
Samuel P. Harbison III, Guy L. Steele Jr.
ISBN 0-13-089592-X (from the back of my copy)
Amazon's listing:
ISBN-10 ‏ : ‎ 9780130895929
ISBN-13 ‏ : ‎ 978-0130895929

The selling price at Amazon is way too high. Find a used copy for a reasonable price.

This is NOT a cover-to-cover read for people to learn C. It is a reference book for people who know enough C to write code, but need to look up the subtle behaviors of C language constructs. This book documents exactly how the C compiler behaves in every possible situation, including the pre-processor and all those weird corner cases. I said "every possible situation" and still "assert" that it is true - I never found anything in C that was not covered by this book.
 
C

CrazedNerd

Guest
Most C programmers use makefiles. If you're learning C, they would be a hood thing to get familiar with.
What you are referring to is the use of the program "make" to read makefiles, which can compile/execute code. It's useful if you have a large project and you don't want to keep typing out the build instructions over and over again, and it's also useful for installing software on your computer.

However, it is not useful for running simple c programs. That's because you have to have the pre-determined file names and compiler settings all written out in the makefile. My script here compiles runs very simple C programs easily, and is better for saving beginnners typing (once they understand what each thing in the script does, of course):
Code:
#!/bin/bash

[ -f a.out ] && rm a.out

gcc $1 -lm;

[ -f a.out ] && a.out

I did find a fleshed out beginner tutorial for using make, however, it unfortunately doesn't work beyond the section where it tells you how to compile the files with gcc. Make just gives you a "missing operand" error when you execute make makefile:


However, the solution to the error can be found here, even I'm not knowledgeable enough about make to actually apply the knowledge in the stack exchange:


I'm finding that this tutorial is a lot better...


The first one might be deprecated/old usage

There is no need for "make makefile", because make detects any file labeled makefile automatically, and it's case in-sensitive: you can capitalize makefile but i don't see the point in doing that. Just run "make".
 
Last edited by a moderator:

JasKinasis

Well-Known Member
Joined
Apr 25, 2017
Messages
1,820
Reaction score
2,656
Credits
15,608
I researched that a little bit, is "make" just used for compiling and installation, or can it be used to run the binaries too?

Anyways, during formatting, i accidentally erased all my c stuff (it's better to put everything in your home directory to make backups simpler and more efficient), and as a response decided to make C file creation really easy on myself:

Code:
#!/bin/bash
noArgs $1 #see .bashrc hack above

echo "#include <stdio.h>" >> $1

echo "#include <stdlib.h>" >> $1

echo "int main ()" >> $1

echo -e "{\n}" >> $1

nano $1
Sphen has answered the "What is make?" question.

Regarding your script for generating a C source file when starting a new project:
Rather than putting C code into echo statements in a bash script, you could just have an entire .C file as a template, along with a generic Makefile and config.mk (for configuring make).

Then it's a case of creating a script which takes a project name as a parameter and:
1. Creates a new directory for the project in a particular directory. e.g ~/projects, using the project name as the name of the directory.
2. Copies any template files into the new projects directory.
3. Processes the generic template files to make them more specific to the project.

E.g.
- Have a generic main.c and a generic config.mk and Makefile saved in somewhere like ~/templates/CProject/.
- Set up a ~/projects/ directory to save your C projects in.
And have your project creation/setup script in ~/bin/, or perhaps ~/.local/bin/.

So your C template - ~/templates/CProject/main.c might look something like this:
C:
#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Your C code here!\n");
    return 0;
}

Then we have ~/templates/CProject/config.mk - a simple config file for make:
Code:
# Config settings for make
#
# Installation paths for if you decide to make an install target
#PREFIX = /usr/local
#MANPREFIX = ${PREFIX}/share/man

# Add any paths to 3rd party library includes e.g. -i/usr/X11R6/include
INCS =

#Add any paths to 3rd party shared libs e.g. -L/usr/X11R6/lib
LIBS =

# compiler flags
CFLAGS   = -g -pedantic -Wall -Wno-deprecated-declarations ${INCS}

# linker flags
LDFLAGS = ${LIBS}

# Compiler to use - NOTE cc is a link to the systems default C compiler
# On Linux this usually points to gcc
# But if you've set up clang as your systems default compiler,
# then cc will point to clang and clang will be used instead of gcc
CC = cc
The config.mk above is enough for compiling a simple project.
I've put in some commented out PREFIX and MANPREFIX paths, for if you ever decide to add install, or uninstall targets to your Makefile. You'd use these paths in the Makefile to specify where to copy the binary and the man pages in your install target (or remove them from, for an uninstall target).
Then there is ~/templates/CProject/Makefile - a simple Makefile:
Code:
include config.mk

# Any additional source files you add to your project should be included below
SRC = {projectname}.c
OBJ = ${SRC:.c=.o}

# build targets
all: {projectname}

# Compile the binary '{projectname}' using the list of .o files, calling the compiler with linker flags
{projectname}: ${OBJ}
    ${CC} -o $@  ${OBJ} ${LDFLAGS}

# compile .o object files from any .c files by calling the compiler with cflags
.c.o:
    ${CC} -c ${CFLAGS} $<

# Remove the binary and any object files, so we can build again
clean:
    rm -f {projectname} ${OBJ}

.phony : all clean
Note: In the above, {projectname} is a placeholder in the Makefile template. After our shellscript has copied the template Makefile to the directory for the new project, we’ll use sed to replace all instances of {projectname} with the actual name of the project.

This simple Makefile template will work for single file projects. And if you start adding additional headers and C files to your projects, it should be able to automatically build those too. The Makefile also includes config.mk which sets up some options for make.

So using just those three simple, generic, file templates, you could start any number of new C projects.
So these files will serve as a starting point for any new C project, that you can modify and build upon as your project gets bigger.

Finally, to put it all together, we need a script to create a directory for the project and populate it with copies of main.c, config.mk and a Makefile and remove the placeholders:

The way this will work:
- It needs to take a single parameter, specifying the name of the project.
- It needs to ensure that all of the template files exist at ~/templates/CProject/
- It needs to add a directory called projectname to ~/projects/ - where projectname is the name of the project.
- It needs to copy main.c to the new project directory, renaming it projectname.c. Again, where projectname is the name of the project.
- It needs to copy the config.mk and Makefile to the new project directory.
- It needs to use sed to process the Makefile to replace all instances of {projectname} with the actual project name.
So here's ~/bin/newCProject, which does that:
Bash:
#!/usr/bin/env bash

# your noArgs shortcut
noArgs $1

# paths to templates directory and template files
templatePath="${HOME}/templates/CProject"
configPath="${templatePath}/config.mk"
makefilePath="${templatePath}/Makefile"
mainCPath="${templatePath}/main.c"

# check the templates are all there
if [[ ! -d "$templatePath" ]] || [[ ! -f "${configPath}" ]] || [[ ! -f "${makefilePath}" ]] || [[ ! -f "${mainCPath}" ]]; then
  echo -e "Template directory/files are missing from ${templatePath}!\n"
  exit 1
fi

# create new directory for the project
projectPath="${HOME}/projects/$1"
if [[ ! -d "$projectPath" ]] ; then
    mkdir -p "$projectPath"
fi

# copy the template files to the new projects directory:
# copy main.c template as projectname.c
cp "$mainCPath" "${projectPath}/$1.c"

# copy the config.mk and Makefile templates
cp "$configPath" "$makefilePath" "$projectPath"

# Replace the {projectname} placeholders in the copied Makefile with the project name
sed -i "s/{projectname}/$1/g" "${projectPath}/Makefile"

Then to create a new C project, you’d just run your script passing the project name:
e.g.
Bash:
newCProject MangyMutt

And that will create a directory at ~/projects/MangyMutt/, which will have MangyMutt.c, config.mk and Makefile in them.
The Makefile should have all of the {projectname} placeholders replaced with MangyMutt.
So it would look something like this:
Code:
include config.mk

# Any additional source files you add to your project should be included below
SRC = MangyMutt.c
OBJ = ${SRC:.c=.o}

# build targets
all: MangyMutt

# Compile the binary 'MangyMutt' using the list of .o files, calling the compiler with linker flags
MangyMutt: ${OBJ}
    ${CC} -o $@  ${OBJ} ${LDFLAGS}

# compile .o object files from any .c files by calling the compiler with cflags
.c.o:
    ${CC} -c ${CFLAGS} $<

# Remove the binary and any object files, so we can build again
clean:
    rm -f MangyMutt ${OBJ}

.phony : all clean

Then once you’ve generated your project, you will be able to cd into your project directory and run make, to build the project. And it should build successfully, right off the bat. With no user intervention.

Then you could run the executable:
Bash:
./MangyMutt

Which would yield the output:
Your C code here!

So that's three simple template files and a script and you can now create a new, build-able C project in the blink of an eye.

By using template files with a shell-script, you can quickly set up all kinds of projects.
In this case, we've created something that can generate a new, blank C project, but we're not limited to that.
You could use the same kind of methods/principles to generate C++ projects, Python projects, QT projects (in Python or C++), assembly projects, web-pages, or even an entire website! Basically, whatever you want to do. You just need to have some generic files stored somewhere and then a script to set up the new project.

It might take a little time to set up something generic like this, but if you plan on creating a lot of projects, then creating an automated system like this is time well-spent.
 
Last edited:

sphen

Well-Known Member
Joined
Dec 12, 2022
Messages
871
Reaction score
753
Credits
10,429
Adding a hint about make and makefiles:
The make utility is fussy about <tab> characters vs. <space> characters. The make utility expects tab for indentation, not space.

Some people treat all white space the same, and use spaces interchangeably with tabs. That may not work with make and makefiles. Some editors replace tab characters with space characters automatically, so you may not notice or understand if problems appear.
 


Latest posts

Top