shelllings

a practical way to learn shell
Log | Files | Refs | README | LICENSE

commit cf38bcd2d35fb15a90731289adc232826ef41a1f
Author: David Voznyarskiy <31452046-davidvoz@users.noreply.gitlab.com>
Date:   Sat,  8 Nov 2025 11:39:56 -0800

added rest

Diffstat:
ALICENSE | 24++++++++++++++++++++++++
AREADME.md | 14++++++++++++++
Aexercises/01_echo.sh | 15+++++++++++++++
Aexercises/02_variables.sh | 15+++++++++++++++
Aexercises/03_listing.sh | 20++++++++++++++++++++
Aexercises/04_navigation.sh | 28++++++++++++++++++++++++++++
Aexercises/05_creating.sh | 26++++++++++++++++++++++++++
Aexercises/06_removing.sh | 44++++++++++++++++++++++++++++++++++++++++++++
Aexercises/07_wordcount.sh | 24++++++++++++++++++++++++
Aexercises/08_cat.sh | 22++++++++++++++++++++++
Aexercises/09_direction.sh | 20++++++++++++++++++++
Aexercises/10_piping.sh | 30++++++++++++++++++++++++++++++
Aexercises/11_userinput.sh | 19+++++++++++++++++++
Aexercises/12_arguments.sh | 27+++++++++++++++++++++++++++
Aexercises/13_copying_and_moving.sh | 26++++++++++++++++++++++++++
Aexercises/14_conditionals.sh | 46++++++++++++++++++++++++++++++++++++++++++++++
Aexercises/15_conditionals2.sh | 37+++++++++++++++++++++++++++++++++++++
Aexercises/16_andand_oror.sh | 42++++++++++++++++++++++++++++++++++++++++++
Aexercises/17_exit.sh | 15+++++++++++++++
Aexercises/18_quiz.sh | 37+++++++++++++++++++++++++++++++++++++
Aexercises/19_functions.sh | 37+++++++++++++++++++++++++++++++++++++
Aexercises/20_whileloop.sh | 36++++++++++++++++++++++++++++++++++++
Aexercises/21_forloop.sh | 35+++++++++++++++++++++++++++++++++++
Aexercises/22_file_checks.sh | 28++++++++++++++++++++++++++++
Aexercises/23_man_and_tips.sh | 27+++++++++++++++++++++++++++
Aexercises/24_grep.sh | 32++++++++++++++++++++++++++++++++
Aexercises/25_globbing.sh | 16++++++++++++++++
Aexercises/26_regex.sh | 41+++++++++++++++++++++++++++++++++++++++++
Aexercises/27_regex2.sh | 44++++++++++++++++++++++++++++++++++++++++++++
Aexercises/28_case.sh | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexercises/29_gzip.sh | 9+++++++++
Aexercises/30_tar.sh | 8++++++++
Aexercises/31_trap.sh | 32++++++++++++++++++++++++++++++++
Arun_me.sh | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashelllings.sh | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/01_echo.sh | 17+++++++++++++++++
Atests/02_variables.sh | 15+++++++++++++++
Atests/03_listing.sh | 24++++++++++++++++++++++++
Atests/04_navigation.sh | 31+++++++++++++++++++++++++++++++
Atests/05_creating.sh | 15+++++++++++++++
Atests/06_removing.sh | 30++++++++++++++++++++++++++++++
Atests/07_wordcount.sh | 18++++++++++++++++++
Atests/08_cat.sh | 23+++++++++++++++++++++++
Atests/09_direction.sh | 24++++++++++++++++++++++++
Atests/10_piping.sh | 23+++++++++++++++++++++++
Atests/11_userinput.sh | 22++++++++++++++++++++++
Atests/12_arguments.sh | 21+++++++++++++++++++++
Atests/13_copying_and_moving.sh | 20++++++++++++++++++++
Atests/14_conditionals.sh | 17+++++++++++++++++
Atests/15_conditionals2.sh | 18++++++++++++++++++
Atests/16_andand_oror.sh | 17+++++++++++++++++
Atests/17_exit.sh | 16++++++++++++++++
Atests/18_quiz.sh | 26++++++++++++++++++++++++++
Atests/19_functions.sh | 17+++++++++++++++++
Atests/20_whileloop.sh | 16++++++++++++++++
Atests/21_forloop.sh | 36++++++++++++++++++++++++++++++++++++
Atests/22_file_checks.sh | 17+++++++++++++++++
Atests/23_man_and_tips.sh | 23+++++++++++++++++++++++
Atests/24_grep.sh | 15+++++++++++++++
Atests/25_globbing.sh | 15+++++++++++++++
Atests/26_regex.sh | 21+++++++++++++++++++++
Atests/27_regex2.sh | 22++++++++++++++++++++++
Atests/28_case.sh | 16++++++++++++++++
Atests/29_gzip.sh | 28++++++++++++++++++++++++++++
Atests/30_tar.sh | 27+++++++++++++++++++++++++++
Atests/31_trap.sh | 24++++++++++++++++++++++++
66 files changed, 1746 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org> diff --git a/README.md b/README.md @@ -0,0 +1,14 @@ +# shelllings + +## about +A practical way of learning shell through exercises. +This has a focus on a linux environment. +This is also mainly a way for me to learn shell as well so bugs and unoptimized code should be expected. + +## installing and running + +``` +git clone https://gitlab.com/davidvoz/shelllings.git +cd shelllings +sh run_me.sh +``` diff --git a/exercises/01_echo.sh b/exercises/01_echo.sh @@ -0,0 +1,15 @@ + +# To write the first shell command, see the other files that end with +# .sh and see what they have as their first line at the very top. Do +# the same here. +# +# The first command you will learn is 'echo'. This is the same as printf +# or print or cout in other languages. To print a message, type echo, +# and then surround the message with double quotation marks. +# +# $ echo "Welcome to shelllings" +# +# The '$' symbol above is not needed, it is used to show you what is a +# command. As is custom, write your first Hello World program below + +echo diff --git a/exercises/02_variables.sh b/exercises/02_variables.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Assigning variables in shell go like this +# +# $ project="shellling" +# $ echo "Welcome to $project" # output: Welcome to shellling +# +# Notice that there are no spaces around the '=', and the '$' is being +# used in the echo command. +# +# Fix the command below + +name = Bob +echo "Hello name" # this is a comment too, the program will ignore this + # because of the pound sign diff --git a/exercises/03_listing.sh b/exercises/03_listing.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# In the folder you are in, you can list the folder contents by typing +# 'ls' and seeing what is there. Some shell configs will have the +# folders bold and different colored to help with navigation. +# +# $ ls # lists contents +# $ ls -a # lists hidden contents as well as unhidden +# $ ls -A # same as -a but without . and .. +# Also, not entirely POSIX compliant but many modern shells +# run it fine +# $ ls -l # shows linkages in files, like how we did with +# ls -l /bin/sh +# $ ls -R # recursively goes into folders listing contents out +# $ ls -AR # separate arguments can be combined to execute both +# +# For your exercise, a random file will be created and hidden. Find +# the file and delete it. + +rm file_name # rm means to remove, do not change that part diff --git a/exercises/04_navigation.sh b/exercises/04_navigation.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# When you installed this program, it was written to 'cd' into this +# directory (folder). cd stands for change directory, when you cd'd +# into shelllings/ you changed into that directory. +# +# $ cd foo/ # takes you into the foo folder +# $ cd .. # takes you out of 1 upper folder +# $ cd ../.. # takes you out of 2 upper folders +# $ cd ~ # navigates to your user's home directory, may not +# work with all shells +# $ pwd # shows you which folder path you are in +# +# The exercise will begin when you run 'sh shelllings.sh 3' and you +# will have to find a random folder. Navigate using the commands above +# to find a folder with a password in it. You may want to use the 'ls' +# command from the previous lesson. +# +# When you have found the file, use this command. Replace the file_name +# with the actual file's name if you cannot open it +# +# $ cat file_name +# +# Don't worry about deleting the newly created folders, it should be +# automatically deleted. + +password="" +echo "$password" diff --git a/exercises/05_creating.sh b/exercises/05_creating.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# To create a folder, use the touch command +# +# $ touch file +# $ touch file1 file2 +# +# To create folder, use the make directory command +# +# $ mkdir folder +# $ mkdir folder1 folder2 +# +# You can also create more folders at a single time +# +# $ mkdir -p dir1/dir2 # enables the parent option because without +# it you cannot create more than a layer +# deep +# $ touch dir1/dir2/file # lets you touch a file deeper +# +# For your exercise, fix the code that is meant to create a folder that +# is at least 3 directories deep with a file named 'file' + +mkdir +touch file + +# do not remove the newly created folder just yet diff --git a/exercises/06_removing.sh b/exercises/06_removing.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# The previous exercises have created a mess in this folder. We can +# clean it up using the 'rm' command. +# +# $ rm file # removes file +# $ rm file1 file2 # removes files +# $ rm -r dir/ # recursively removes files and folders +# $ rm -rf dir/ # forces the previous command +# $ rm * # removes all the files within the current folder, +# saves time on not having to input every file +# $ rmdir dir/ # removes an empty directory +# +# The astrisk is a very important symbol in shell programming. Take the +# example below +# +# $ rm folder/*.txt +# +# This command will rm all the files within folder/ that end with .txt +# meaning anyfile that ends with .c, .sh, or has no dot endings will +# not be deleted. +# +# $ rm a*z +# +# The above command will remove any combination of files that start with +# 'a' and end with 'z'. Including files directly named 'az' with nothing +# inbetween. +# +# For your next exercise, remove the custom folder you created earlier +# using the commands above directly in the terminal. An extra folder +# will be created and try to remove everything in the most efficient +# way possible when typing. Don't change anything in the code snippet +# below +rm -rf 06_folder +mkdir 06_folder +for i in $(seq 1 100); do + touch 06_folder/file$i +done + +# in one line below, find a way to delete all the 100 files created +# from above without deleting the folder. Test out different ways, +# maybe combine arguments to accomplish this. Do not add more or +# less than 44 lines of code in this file. + diff --git a/exercises/07_wordcount.sh b/exercises/07_wordcount.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# wc, word count, is a program that measures and counts bytes, +# characters, and lines +# +# $ wc -l file # counts the amount of lines in a file +# $ wc -w file # counts the amount of words in a file +# $ wc -m file # characters +# $ wc -c file # bytes +# $ wc file # gives out the lines, words, and bytes of a file +# +file="LICENSE" +word_count=$(wc -w $file) +line_count= # fix this +byte_count= # fix this + +echo "$line_count" +echo "$word_count" +echo "$byte_count" + +# One more thing you should get used to is standard input. Run the wc +# command without an argument and it will take you to a new line. Type +# whatever you want and end it with a ctrl d. You can run wc -w into +# this environment as well to see the length of your sentence. diff --git a/exercises/08_cat.sh b/exercises/08_cat.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# We briefly mentioned the cat command earlier. It prints out all the +# data of a file into the terminal. And like the animal, 2 more commands +# are used. Head to print out the first lines of a file and tail to +# print out the last lines of a file. +# +# $ cat file # prints out the entiretly of a file +# $ head file # prints out the first 10 lines of a file +# $ tail file # prints out the last 10 lines of a file +# $ head -n 1 # prints out only the first line of a file +# $ tail -n 18 # prints out only the last 18 lines of a file +# $ head -n -4 # prints out all lines except the last 4 lines +# $ tail -n +9 # prints out from line 9, onwards +# +# Fix the command below to do a word count on the last 50 lines of the +# LICENSE file in this repository. Don't change the new file name. + +head 50 LICENSE > 08_cat # the '>' sign is discussed later. For now +# know that the command should be outputting +# the last 50 lines into a file called 08_cat +wc -l 08_cat diff --git a/exercises/09_direction.sh b/exercises/09_direction.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# You can direct data to go where you want with these commands. +# +# $ echo "message" > file # 'message' is the only data in the file +# if there is extra data, it is erased +# $ echo "message" >> file # 'message' is ADDED ONTO the end of +# file with the previous data present +# $ command 2> file # only direct message errors into file +# $ command 2>> file # appends only error messages into file +# $ command > file 2>&1 # directs standard output (>) and error +# messages (2>) into file +# $ command >> file 2>&1 # same as before but appends +# +# Let's say for whatever reason you want to add all the contents of the +# exercises/ folder and the license into one file. Fix the commands +# below + +cat exercises >> 09_file +cat LICENSE > 09_file diff --git a/exercises/10_piping.sh b/exercises/10_piping.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# We used this as an example for data direction and up until this point +# we had to run separate commands. +# +# $ head -n 50 LICENSE > 09_file +# $ wc -w 09_file +# +# But now we can combine this using pipes. The '|' symbol is used to +# basically accomplish what was shown before +# +# $ head -n 50 LICENSE | wc -w +# +# Notice that we did not have to add a file onto wc -l or even enter +# a standard input environment. +# +# As for the actual exercise, change all of these commands into pipes. +# NOTE the testing of this case is very sensitive, try being concise +# and leave unnecessary whitespace out of your answers. Leave the white +# space needed for human readability. + +cat LICENSE > file +wc -w file + +ls -A "$HOME" > file +wc -l file + +# As a fun exercise, run that command above and see how cluttered your +# home directory is. In fact, in many modern shells, "$HOME" isn't +# needed, '~' is enough. 'ls -A ~' will list out your home directory diff --git a/exercises/11_userinput.sh b/exercises/11_userinput.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# We can go on ahead and read user input easily +# +# $ echo "Enter your name below" +# $ read user_input +# $ echo "$user_input" +# +# Unfortunately echo will take the user input into a new line. To avoid +# that you can use printf (like in C) or, less posix compliant, use +# 'echo -n' to avoid that new line +# +# $ printf "Enter your name: " # equivalent as the next line +# $ echo -n "Enter your name: " +# +# Fix this program that is meant to input the user's input into a file + +printf "Enter your sentence: " +read user_input > 11_file diff --git a/exercises/12_arguments.sh b/exercises/12_arguments.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# When you run a shell, you can input arguments into the .sh file to be +# used inside of it. +# +# $ sh test.sh file_name +# +# And then within test.sh, we can have code like this +# +# $ wc -l $1 +# +# The arguments that can be into a script are read as follows +# +# $0 # the script name +# $1 # the first argument +# $2 # the second argument +# $n # the nth argument +# $@ # all arguments as separate words +# $* # all arguments as a single word +# $# # number of arguments passed +# +# Fix below if the arguments passed were: ugly Jayce smelly tofu + +echo "One day, [insult] [name] left a [adjective] [food] in his pocket." +echo "A tiger smelt him and ate him." +echo "The words you used were: $" +echo "The number of words you used were: $" diff --git a/exercises/13_copying_and_moving.sh b/exercises/13_copying_and_moving.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# You can copy files and move them from one location to another. When +# you "move" a file, you are changing its location. When you copy a +# file, within the same filesystem, this is just renaming the file path. +# When you copy a file, you create a new, separate copy of the file's +# contents in another location. +# +# $ cp file1 file2 # this copies the contents, permissions, +# and metadata +# $ cp file dir/ # copies the file into a directory +# +# $ mv file /to/new/path/ # moves the file to a new location +# $ mv old_name new_name +# +# Often if you really don't want the metadata, and just want to have +# the contents copied, you can use the cat command +# +# $ cat file_withmetadata > new_file +# +# Fix this program that is built off from the previous exercise by +# having the file be renamed into a file named 11_user_input +printf "Enter your sentence: " +read user_input +echo "$user_input" > 11_file + diff --git a/exercises/14_conditionals.sh b/exercises/14_conditionals.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# As with other programming languages, we can evaluate if conditions or +# statements are true or false. In this exercise we will be dealing with +# strings and basic if/else structure. Below are 3 examples of this. +# +# $ option="yes" +# $ if [ "$option" = "yes" ]; then +# $ echo "option enabled" +# $ fi +# +# $ password="shellling123" +# $ if [ "$name" = "shellling123" ]; then +# $ echo "access granted" +# $ else +# $ echo "access denied" +# $ fi +# +# $ printf "Enter name: " +# $ read name +# $ if [ "$name" = "shellling" ]; then +# $ echo "Welcome!" +# $ elif [ "$name" = "shelllings" ]; then +# $ echo "Welcome all!" +# $ else +# $ echo "You are not welcome here." +# $ fi +# +# Notice that there is spaces between the square brackets and that, for +# strings, there are quotation marks around the variables. There are +# more options than just '=' +# +# = # strict string comparison of equals +# != # strict string comparison of does not equal +# -z # checks if string is empty +# -n # checks if string is not empty +# +# Fix the incomplete script below, leave all the echo lines unchanged + +if # check if user input is or is not empty here + if [ "$1" = "password123" ] then + echo "Welcome" + else + echo "Access not granted" +else + echo "Password not entered" diff --git a/exercises/15_conditionals2.sh b/exercises/15_conditionals2.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# The previous if-conditionals were string comparisons. We will now take +# a look into using number comparisons. +# +# -eq # equal to +# -ne # not equal to +# -gt # greater than +# -ge # greater than or equal to +# -lt # less than +# -le # less than or equal to +# +# Also, when you input a number from the 'read' command, it +# automatically becomes a integer. Shell cannot use float or doubles. +# To use decimal numbers, you must delegate it to other tools that will +# be taught much later +# +# $ read user_input +# $ if [ $1 -eq 1412 ]; then +# $ echo "Correct pin" +# $ fi +# +# You can also do operations but these must be surrounded by double +# parenthesis. +# +# $ $((10 + 2)) # 12 addition +# $ $((10 - 2)) # 8 subtraction +# $ $((10 * 2)) # 20 multiplication +# $ $((10 / 2)) # 5 division +# $ $((-10 % 3)) # -1 modulo, finds out the remainder +# +# Write a script that tells you if an inputted number is even or odd. + +num=$1 + + echo "odd" + echo "even" diff --git a/exercises/16_andand_oror.sh b/exercises/16_andand_oror.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# We used the if/else case but that is often not needed in shell. We can +# instead use && and || for many cases. Take a look at the snippet below +# +# $ sh foo.sh && sh bar.sh +# +# The snippet above means that if 'sh foo.sh' is ran successfully, +# without error, then 'sh bas.sh' will be ran. +# +# $ sh foo.sh || sh bar.sh +# +# If 'sh foo.sh' is ran and an error does occur, then 'sh bar.sh' is +# ran. +# +# Some conditions require square brackets, these are test expressions. +# Examples of conditions that require square brackets are usually string +# and string comparisons, existence of files, and empty or not files. +# +# $ mv file1 file2 || echo "Could not be moved" +# $ [ -z "$1" ] || echo "Nothing was entered" +# +# You can also extend it +# +# $ [ "$1" = "password123" ] || { echo "power off"; sh shutdown.sh; } +# +# The semi colon is used the same way as other programming languages +# where you can combine multiple lines into a single line. Note that +# it should still be easily readable to humans to debug and understand. +# If you go back to exercise 14, you can see them being used to condense +# the 'do' word +# +# Fix the code below so the echo commands make sense with the pin 1412 +# Leave the two && in place + +[] && echo "incorrect pin" +[] && echo "correct pin" + +# You can also combine it into 1 line but it doesn't always comply with +# many shells +# +# $ [] && echo "good" || echo "bad" diff --git a/exercises/17_exit.sh b/exercises/17_exit.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Behind the scenes for all your 'sh shelllings.sh', exit codes are +# being used to evaluate if your exercises were successful. Like earlier +# when we were using && and ||, we can manually say if a code has been +# unsuccessful or successful. +# +# $ exit 1 # error +# $ exit 0 # no error +# +# The keyword 'exit' can be used to stop a program from continuing if +# it has +# +# Write a script below where if someone has 1412 as the argument when +# calling the script, it will be successful diff --git a/exercises/18_quiz.sh b/exercises/18_quiz.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# Quiz 1 Notes program +# +# This program is meant to store notes but has some issues + +NOTES_FILE=notes.txt + +# a bug always has '-p', when selected, added to notes.txt +if [ "$*" = "-p"]; then + cat notes.txt +fi + + +if [ "$#" -ge 1] then + echo "[$(date)]" >> "$NOTES_FILE" # this line is without fault + echo "" >> "$NOTES_FILE" +else + echo "Nothing was added" +fi + +# When you are done, change the NOTES_FILE location and consider putting +# this into your .local/bin with this part added (yes, you have to find +# a way to have this inserted into the code above). +# +# $ nvim +startinsert /tmp/note_temp +# $ if [ -s /tmp/note_temp ]; then +# $ echo "[$(date)]" >> "$NOTES_FILE" +# $ cat /tmp/note_temp >> "$NOTES_FILE" +# $ echo "" >> "$NOTES_FILE" +# $ rm /tmp/note_temp +# $ fi +# +# Don't forget to have that directory path exported as a path in your +# shell config. Usually, like this +# +# export PATH="$HOME/.local/bin:$PATH" diff --git a/exercises/19_functions.sh b/exercises/19_functions.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# There are many times where you would need to repeat large sections of +# code. Or need to simplify the code for readability and organization. +# Functions would be use in this case, this is a simple example of how +# to use a function. +# +# $ greet() { +# $ name="$1" +# $ echo "Hello, $name." +# $ } +# $ +# $ greet Alice # Hello, Alice. +# $ greet Bob # Hello, Bob. +# +# Pay attention to the syntax to use this function. +# +# Fix this program + +display_entropy() { + echo "Entropy available: $(cat /proc/sys/kernel/random/entropy_avail)" + echo "Entropy pool size: $(cat /proc/sys/kernel/random/poolsize)" + echo "Wakeup threshold: $(cat /proc/sys/kernel/random/write_wakeup_threshold)" + echo "Minimum reseeding in seconds: $(cat /proc/sys/kernel/random/urandom_min_reseed_secs)" +} + +help() { + echo "Usage: sh 19_functions.sh [OPTION]" + echo "-en displays the entropy information of your system" + echo "-h, --help displays this help message" +} + +# Write a program where if something runs the program with the option +# '-de' at the end (sh 19_functions.sh -de) it would call the +# display_entropy function. Anything else written, would have the help +# function ran. + diff --git a/exercises/20_whileloop.sh b/exercises/20_whileloop.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +# A while loop is a way to run a command over and over again while a +# condition is true. The condition can be an evaluation or simply the +# word 'true'. Take these examples +# +# $ i=10 +# $ while [ "$i" -ge 1 ]; do +# $ echo "$i" +# $ i=$((i - 1)) +# $ done +# +# The snippet above lists out all the integers from 10 until i equals +# 0. You can also use 'break' to stop that while loop +# +# $ evil_number=36 +# $ count=1 +# $ while true; do +# $ [ ! $count -eq $evil_number ] && echo "Not evil number" +# $ [ $count -eq $evil_number ] && break +# $ done +# $ echo "Evil number found" +# +# For your challenge, complete this game for the user. Do not change +# the correct number. + +magic_number=2987133075769857928 +echo "I'm thinking of a number between 1 and four hundred quintillion." + +while True; + printf "Take a guess: " + read user_input + [ $magic_number -eq $user_input ] && break + echo "Nope, wrong." + +echo "Got it, cheater" diff --git a/exercises/21_forloop.sh b/exercises/21_forloop.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# For loops follow a logic where it does an operation for every instance +# +# $ for var in list; do +# $ operation +# $ done +# +# From the semi-colon used, you can see that the snippet above can be +# 4 lines long, but it is generally condensed using the semi-colon. +# +# Let's say you want to wipe the metadata from a set of photos in a +# folder you are in, it follows +# +# $ for pic in *.jpg; do +# $ exiftool -all= "$pic" +# $ done +# +# Fix the program below. If you mess up the test/ folder, you can git +# clone the project and move it in. Hopefully the annoyance will cause +# you to be more careful when running these commands. +# +# You want to add a line at the very end of every file, but you also +# want to delete it. Don't worry about deleting the '# bloat message' +# from the tests/ folder, as the program should delete all lines like +# that + +for file in tests/*; do + echo "# bloat message" >> "$test_file" +done + +for file in tests/*; do + tail -n -1 "$test_file" > "$test_file".tmp + mv -f "$test_file".tmp "$test_file" # this line is valid +done diff --git a/exercises/22_file_checks.sh b/exercises/22_file_checks.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# When writing scripts and into files, there are times where you have +# to verify that the file exists, the file is writable, or you need to +# know if a folder fulfills the same requirements. These commands below +# can do that +# +# -e returns true if the file exists +# -d true if it's a directory +# -f true if the file exists and is a regular file +# -r true if the file is readable +# -w true if the file is writable +# -x true if the file is an executable +# -s true if the file exists and is not empty +# +# $ [ -e LICENSE ] && echo 'exists' +# +# Find me files that can make the statement true so then echo works. +# Don't edit the echo messages + +touch 22_file + +[ ] && echo "Exists" +[ ] && echo "Directory exists" +[ ] || echo "empty" # have 22_file be here somehow +[ ] || echo "Not an executable" + +rm 22_file diff --git a/exercises/23_man_and_tips.sh b/exercises/23_man_and_tips.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# At this point, you have learned many commands that are used and you +# won't be able to remember them all at the top of your head. Instead of +# going into your notes on into this folder, use the '--help' +# (sometimes '-h') to see the basic usage or the 'man' command to get +# a more detailed documentation +# +# $ cat --help # displays a basic help message +# $ man echo # enters you into a more detailed portal +# +# You should try out those commands right in the terminal. +# +# If there are more specific use cases or you only need a few commands +# that you keep forgetting, consider using the 'ffr' command located at +# https://github.com/davidvoz/toyotacorolla/blob/main/ffr +# For the linux users here, copy that code into .local/bin/ffr and add +# the line below to your .bashrc or .zshrc. Follow the instructions on +# the script and try it out. +# +# $ export PATH="$HOME/.local/bin:$PATH" +# +# To keep this exercise simple, figure out how many lines the man page +# for cat uses, and the amount of words in ls's help command + +echo "$amount_cat" # leave this unchanged +echo "$amount_ls" # leave this unchanged diff --git a/exercises/24_grep.sh b/exercises/24_grep.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# grep is a command that searches for text in files or input. +# +# $ grep "the" LICENSE +# +# It will show you all the times the letters 't', 'h', and 'e' are used +# next to each other. If you want to only find that word we can do this +# +# $ grep -w -i "the" LICENSE # -w for the word, -i to ignore the +# # the case spellings +# +# The common useful options are listed here +# +# -w matches whole words +# -x matches whole lines +# -i ignores case spellings +# -v invert match findings +# -n show line numbers +# -c count matches per line +# -l prints file names with matches +# -q quiet, supress the output, used often for true or false findings +# -H show the filename before a match when matching many files +# -o prints only the matching part, not whole line +# +# Help me figure out a way where I can find the amount of times the word +# 'the' (in all of its strange uppercases and lowercases) is used in +# the LICENSE file. It seems that I can't get quite an accurate reading. + +grep 'the' LICENSE | wc + +echo "$word_count" # leave be diff --git a/exercises/25_globbing.sh b/exercises/25_globbing.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# Take a look at what globbing is in unix +# +# $ cat * # cats out everything immediate in your folder +# $ ls 1?_* # the ? matches everything to a single character, +# # while the * matches it to anything. Run this +# # command in the exercises/ or tests/ folder +# $ ls file[abc] # matches only the characters in the brackets +# # so filea fileb and filec will show +# $ ls file[!AB] # matches files without A or B, so fileC and fileD +# +# Find a way to get a list of all the files in tests/ that are numbered +# between 10 and 16 with just one line of code + +ls tests/[]* diff --git a/exercises/26_regex.sh b/exercises/26_regex.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# Regular expression (regex) is also pattern searching through file +# names but also through text or any string. +# +# . matches any single character except newline. +# $ ls tests/ | grep 2. # if you want to see it tested +# +# +# ^ matches the start of a line +# $ grep ^"Long, long" file # same as below +# $ grep "^Long, long" file +# +# +# $ matches the end of a line +# $ grep end$ file +# +# [-] gives a range within the brackets, a-z A-Z 0-9 can be used +# $ ls 1[0-6]* # an easier way of completing the previous +# # exercise without globbing +# +# + matches one or more of the previous character +# $ ls tests/ | grep -E 'o+' # for regex expressions like this you +# # may have to have the -E option +# +# ? matches zero or more of the previous character +# $ ls tests/ | grep -E 'o+*' # not a helpful example +# +# You want to verify if someone has inputted a valid file from tests/ +# Your job is to match the file to a file if the user either inputs a +# number associated with that file, or the name between the number and +# file extension. Meaning inputs with .sh should return an error. + +verify() { + # duplicate more or remove lines below if needed + ls tests/ | grep -E && exit 0 + ls tests/ | grep -E && exit 0 +} + +verify $1 +exit 1 diff --git a/exercises/27_regex2.sh b/exercises/27_regex2.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# Continuing off of regex +# +# [] matches with any one character from inside the brackets +# [^] matches with any that are not in the brackets +# +# () groups parts of a pattern +# +# {} repeats what was shown before a certain amount of times +# [a-zA-Z]{2,} All alphabet characters (lower and uppercase) +# should be used 2 or more times +# [a-zA-Z0-9]{5,15} All characters in square brackets should be used +# a min of 5 times and max of 15 +# +# `` same as $(...), but $() is preferred +# \ backslash for the literal character +# + +# We will verify valid email addresses below. We will keep it simple to +# addresses with one word beginnings with numbers and letters, @, +# and a website domain. +# billybob@bob1.com, David2@test.xyz, etc + +verify() { + # don't forget the + symbol in some of these regex + + # verify that the beginning has valid characters + echo "$1" | grep -Eq || exit 1 + + # verify that an @ symbol and a second level domain is present with + # only letters and numbers + echo "$1" | grep -Eq || exit 1 + + # verify a dot is present + echo "$1" | grep -Eq || exit 1 + + # verify the ending is a top level domain with 2 or more lowercase + # characters + echo "$1" | grep -Eq || exit 1 +} + +verify $1 +exit 0 diff --git a/exercises/28_case.sh b/exercises/28_case.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +# Instead of using if, elif, elif, elif, else, there's a more concise +# (and often faster) method you can use; case. +# +# $ echo "Enter a letter: " +# $ read user_input +# $ +# $ case "$user_input" in +# $ [a-z]) +# $ echo "Entered: lowercase" +# $ ;; +# $ [A-Z]) +# $ echo "Entered: uppercase" +# $ ;; +# $ [0-9]) +# $ echo "I said letter, not number" +# $ ;; +# $ *) # for everything else +# $ echo "What?" +# $ ;; +# $ esac +# +# The snippet above should be self explanatory. Now, for other cases, it +# can look like this +# +# $ case "$item" in +# $ apple|banana|orange) # apple or banana or orange +# $ echo "Food item" +# $ ;; +# $ book) +# $ echo "leisure item" +# $ ;; +# $ *) +# $ echo "unknown" +# $ ;; +# $ esac +# +# It's better to use 'case' instead of 'if' when you are pattern +# matching, which means you can avoid using grep. It can be better to +# do 'if' instead of 'case' when there's arithmetic, like playing a +# hotter/colder game by having a user try to find a number. +# +# Below is the unfinished work of a rock-paper-scissors game between +# a player and the computer + +choices="rock paper scissors" +computer_choice=$(echo "$choices" | tr ' ' '\n' | shuf -n 1) +printf "Choose rock paper or scissors: " +read player_choice + +# The code snippet above needs no changing. +# Have all the echo statements be just those 3 words exactly +case "$player_choice" in + rock) + case "$computer_choice" in + rock) echo "tie" ;; # this is a valid way of condensing code + paper) echo "lost" ;; + scissors) echo "won" ;; + *) + echo "ERROR invalid option" + ;; +esac diff --git a/exercises/29_gzip.sh b/exercises/29_gzip.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# gzip (gunzip) is a GNU file compression and expansion tool that comes +# with most linux distrobutions. Use the --help option to know how to +# compress the LICENSE file into a new file LICENSE.gz, leaving LICENSE +# unchanged. If successful, don't worry about having to remove the .gz +# file, shelllings.sh will take care of that. + + diff --git a/exercises/30_tar.sh b/exercises/30_tar.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# tar is another GNU compression tool that will be more likely used. +# It's more often used in compression of multiple files and folders. +# Same as before, using the --help option, try to find the problem with +# the command below. Don't worry about deleting the .tar.gz file. + +tar -czf exercises/ tests/ shelllings.sh shelllings.tar.gz diff --git a/exercises/31_trap.sh b/exercises/31_trap.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# 'trap' is a very useful tool that I've been using on the exercises +# where I said you didn't have to worry about deleting the created file. +# +# $ trap command SIGNAL +# +# For example, in your tests/ files, many will have trap statements +# +# $ cleanup() { +# $ [ -f "file" ] && rm file +# $ } +# $ trap cleanup EXIT +# +# exit is the signal for trap to call cleanup(). Below are the different +# signals that trap detects +# +# $ trap command EXIT # catches both exit 1 and exit 0 +# $ trap command INT # catches ctrl+c kills +# $ trap command TERM # catches a signal terminate, like 'kill' +# $ trap command INT EXIT # catches different instances +# +# You don't have to call functions when running trap, you can run +# commands in the same line +# +# $ trap 'echo "Exiting.."; exit' TERM +# +# Below is a simple creation of a file, write a trap line that prints +# out "Removing..." and removes the file + +touch file.txt +echo "kill me" > file.txt diff --git a/run_me.sh b/run_me.sh @@ -0,0 +1,83 @@ +#!/bin/sh +BLUE="\033[34m" +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +pause() { + printf "$1" + read user_input + echo "" +} + +pause "Welcome to Shelllings [press ENTER]" + +echo "This program is meant to help you understand most posix shells," +echo "like bash or zsh. You already ran the command 'sh run_shell.sh'" +echo "but the command 'sh' doesn't really exist on most systems." +echo "Run 'ls -l /bin/sh' to see if there is a link to another program." +pause "Let's test it out [press ENTER for ls -l /bin/sh]" + +printf "${BLUE}$(ls -l /bin/sh)${RESET}" +echo "" + +USER_SHELL=$(ls -l /bin/sh | awk '{print $NF}') + +echo "" +if [ "$USER_SHELL" = "sh" ]; then + echo "Looks like you are actually using sh, not everyone has it" + printf "like this. Many shells are changed. " +else + echo "From the command above, we can see that you are actually" + printf "running ${BLUE}$USER_SHELL${RESET} under the hood. " +fi + +echo "Bash is switched to a " +echo "POSIX-compatible mode, dash also only implements POSIX features." +echo "There are many different shells but we will be sticking with" +echo "POSIX compliant programs and commands. Meaning just about" +echo "every shell you use will be able to understand and run these" +echo "commands. This will not always be the case, however." +pause "[press ENTER]" + +printf "The LICENSE file is going to be used often later, ${RED}don't\n" +printf "remove the LICENSE file${RESET}. Thanks." +pause "" + +echo "To use the program shelllings, let's run the command" +echo "'sh shelllings.sh test' to see if it's able to be run" +pause "[sh shelllings.sh test]" + +if sh shelllings.sh test; then + printf "${GREEN}Passed${RESET}" + echo "" +else + printf "${RED}ERROR failed${RESET}" + echo "" + exit 1 +fi + +echo "" +echo "No error message meant that it has not failed. Lets continue on" +pause "and see what exercises there are right now [ls exercises]" + +all_ex=$(ls exercises) +printf "${BLUE}$all_ex${RESET}" +echo "" + +echo "" +pause "[press ENTER]" + +amount=$(ls exercises | wc -l) + +echo "Looks like there are $amount exercises. You should know that" +echo "you can skip through the ones you already know or come back to" +echo "ones you already did. You can run 'sh shelllings.sh n' where n" +echo "is the numbered exercise. You don't need to type the 0's in" +echo "front. Let's get started with the first exercise" +pause "[sh shelllings.sh 1]" + +sh shelllings.sh 1 || { + echo "Looks like it failed. Go on over to the exercises/ folder and" + printf "fix it then run ${GREEN}sh shelllings 1${RESET} again.\n" +} diff --git a/shelllings.sh b/shelllings.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +EX_DIR="exercises" +TEST_DIR="tests" + +list_prev() { + echo "Listing all challenges" + + for file in "$TEST_DIR"/*; do + name=$(basename "$file") + + case "$name" in + 11_userinput.sh|13_copying_and_moving.sh) + echo "$name: skipped" + continue + ;; + esac + + sh "$file" > /dev/null 2>&1 || echo "$name: failed" + sh "$file" > /dev/null 2>&1 && echo "$name" + done + + rm -rf blueberry 11_file .youfoundme +} + + +run_ex() { + num="$1" + ex_file=$(ls "$EX_DIR"/*.sh | sort | sed -n "${num}p") + test_file=$(ls "$TEST_DIR"/*.sh | sort | sed -n "${num}p") + + if [ ! -f "$ex_file" ]; then + echo "ERROR exercise file not found" + exit 1 + fi + + if [ ! -f "$test_file" ]; then + echo "ERROR test file is not found" + exit 1 + fi + + echo "Running exercise: $(basename "$ex_file")" + echo "Testing with: tests/$(basename "$test_file")" + echo "" + + sh "$test_file" +} + +usage() { + echo "Usage" + echo " sh shelllings.sh INT run the INTth exercise" + echo " sh shelllings.sh list list the exercises you have completed" + echo " sh shelllings.sh test test the program if runnable" + exit 0 +} + +case "$1" in + test) + echo "shelllings.sh is working (for now)" + ;; + list) + list_prev + ;; + ''|*[!0-9]*) + usage + ;; + *) + run_ex "$1" + ;; +esac diff --git a/tests/01_echo.sh b/tests/01_echo.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +output="$(sh exercises/01_echo.sh)" + +[ -z "$output" ] && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/02_variables.sh b/tests/02_variables.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ "$(sh exercises/02_variables.sh)" = "Hello Bob" ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/03_listing.sh b/tests/03_listing.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +[ -f ".youfoundme" ] || touch .youfoundme + +cleanup() { + [ -e .youfoundme ] && rm -f .youfoundme +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/03_listing.sh || failed + +[ -f ".youfoundme" ] && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/04_navigation.sh b/tests/04_navigation.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup() { + [ -d "blueberry" ] && rm -rf blueberry +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +mkdir -p blueberry/strawberry/grape/lemon +mkdir -p blueberry/strawberry/cherry/ + +touch blueberry/strawberry/grape/lemon/.you_found_me.txt +echo "The password is grannyApples" > blueberry/strawberry/grape/lemon/.you_found_me.txt + +echo "Find the grape folder, the file with the password might" +echo "be hard to find, but it is there." + +if [ "$(sh exercises/04_navigation.sh)" = "grannyApples" ]; then + printf "${GREEN}Passed${RESET}\n" +else + failed +fi diff --git a/tests/05_creating.sh b/tests/05_creating.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +sh exercises/05_creating.sh + +if find . -mindepth 4 -type f -name file | grep -q .; then + printf "${GREEN}Passed${RESET}\n" +else + printf "${RED}Failed${RESET}\n" + exit 1 +fi diff --git a/tests/06_removing.sh b/tests/06_removing.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +cleanup() { + [ -d 06_folder ] && rm -rf 06_folder +} +trap cleanup EXIT + + +sh exercises/06_removing.sh + +[ "$(wc -l < exercises/06_removing.sh)" -eq 44 ] || { + failed + echo "not 27 lines" +} + +[ -d "06_folder" ] || failed + +[ "$(ls -A 06_folder | wc -l)" -eq 0 ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/07_wordcount.sh b/tests/07_wordcount.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +output=$(sh exercises/07_wordcount.sh) +answer=$(wc -l LICENSE; wc -w LICENSE; wc -c LICENSE) + +[ "$output" = "$answer" ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/08_cat.sh b/tests/08_cat.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup() { + [ -f "08_cat" ] && rm 08_cat +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +output=$(sh exercises/08_cat.sh) +answer=$(tail -n 50 LICENSE | wc -w) + +[ $(wc -w < 08_cat) -eq $answer ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/09_direction.sh b/tests/09_direction.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup(){ + [ -f "09_file" ] && rm 09_file +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/09_direction.sh +output=$(wc -l < 09_file) +answer=$(cat LICENSE exercises/* | wc -l) + +[ $output -eq $answer ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/10_piping.sh b/tests/10_piping.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup(){ + [ -f "10_piping" ] && rm 09_piping + [ -f "file" ] && rm file +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ $(grep "cat LICENSE | wc -w" exercises/10_piping.sh | wc -l) -eq 0 ] && failed + +[ $(grep "ls -A \"\$HOME\" | wc -l" exercises/10_piping.sh | wc -l) -eq 1 ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/11_userinput.sh b/tests/11_userinput.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup(){ + [ -f "11_file" ] && rm 11_file +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/11_userinput.sh + +[ -s 11_file ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/12_arguments.sh b/tests/12_arguments.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +output=$(sh exercises/12_arguments.sh ugly Jayce big tofu) +first_line=$(sh exercises/12_arguments.sh ugly Jayce big tofu | head -n 1) +correct_first_line="One day, ugly Jayce left a big tofu in his pocket." + +[ "$first_line" != "$correct_first_line" ] && failed +[ "$(echo "$output" | grep 4)" != "The number of words you used were: 4" ] && failed +[ "$(echo "$output" | grep "The words")" != "The words you used were: ugly Jayce big tofu" ] && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/13_copying_and_moving.sh b/tests/13_copying_and_moving.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + [ -f "11_file" ] && rm 11_file + [ -f "11_user_input" ] && rm 11_user_input + printf "${RED}Failed${RESET}\n" + exit 1 +} +trap failed EXIT + +sh exercises/13_copying_and_moving.sh + +[ -f "11_file" ] && failed + +[ -s "11_user_input" ] && printf "${GREEN}Passed${RESET}\n" diff --git a/tests/14_conditionals.sh b/tests/14_conditionals.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ "$(sh exercises/14_conditionals.sh)" != "Password not entered" ] && failed # no password entered +[ "$(sh exercises/14_conditionals.sh password123)" != "Welcome" ] && failed # correct password +[ "$(sh exercises/14_conditionals.sh password)" != "Access not granted" ] && failed # incorrectpassword + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/15_conditionals2.sh b/tests/15_conditionals2.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ "$(sh exercises/15_conditionals2.sh 10)" = "even" ] || failed +[ "$(sh exercises/15_conditionals2.sh -1)" = "odd" ] || failed +[ "$(sh exercises/15_conditionals2.sh -9)" = "odd" ] || failed +[ "$(sh exercises/15_conditionals2.sh 1000000)" = "even" ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/16_andand_oror.sh b/tests/16_andand_oror.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ "$(sh exercises/16_andand_oror.sh 1412)" = "correct pin" ] || failed +[ "$(sh exercises/16_andand_oror.sh 9999)" = "incorrect pin" ] || failed +[ $(grep "&&" exercises/16_andand_oror.sh | wc -l) -eq 6 ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/17_exit.sh b/tests/17_exit.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +$(sh exercises/17_exit.sh 9999) && failed +$(sh exercises/17_exit.sh 1412) || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/18_quiz.sh b/tests/18_quiz.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +cleanup() { + [ -f "notes.txt" ] && rm notes.txt +} +trap cleanup EXIT + +sh exercises/18_quiz.sh get oreos for protein shake > /dev/null || failed +sh exercises/18_quiz.sh -p this line should appear > /dev/null || failed +sh exercises/18_quiz.sh -p > /dev/null || failed +sh exercises/18_quiz.sh > /dev/null || failed + +[ ! $(grep "get oreos" notes.txt | wc -l) -eq 1 ] && failed +[ ! $(grep "\-p" notes.txt | wc -l) -eq 1 ] && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/19_functions.sh b/tests/19_functions.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/19_functions.sh -de | grep -q "Minimum reseeding in seconds:" || failed +sh exercises/19_functions.sh -de | grep -q "displays the entropy" && failed +sh exercises/19_functions.sh | grep -q "displays the entropy" || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/20_whileloop.sh b/tests/20_whileloop.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +printf "123\n456\n2987133075769857928\n" | sh exercises/20_whileloop.sh | grep -q "Nope" || failed +printf "123\n456\n2987133075769857928\n" | sh exercises/20_whileloop.sh | grep -q "cheater" || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/21_forloop.sh b/tests/21_forloop.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + + for file in tests/*; do + sed -i '/^# bloat message$/d' $file + done + + [ -e .tmp ] && rm .tmp + + exit 1 +} + +sh exercises/21_forloop.sh || failed + +for file in tests/*; do + name=$(basename "$file") + + case "$name" in + 21_forloop.sh) + continue + ;; + esac + + [ "$(grep "bloat message" "$file" | wc -l)" -ne 0 ] && failed + +done + + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/22_file_checks.sh b/tests/22_file_checks.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +for keyword in Exists Directory empty Not; do + sh exercises/22_file_checks.sh | grep -q "$keyword" || failed +done + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/23_man_and_tips.sh b/tests/23_man_and_tips.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +check() { + read amount_cat + read amount_ls + + [ "$amount_cat" -eq 73 ] || failed + [ "$amount_ls" -eq 867 ] || failed +} + +sh exercises/23_man_and_tips.sh | check + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/24_grep.sh b/tests/24_grep.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ "$(sh exercises/24_grep.sh)" -eq 13 ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/25_globbing.sh b/tests/25_globbing.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +[ "$(sh exercises/25_globbing.sh)" = "$(ls tests/1[0123456]*)" ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/26_regex.sh b/tests/26_regex.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/26_regex.sh 1 > /dev/null 2>&1 || failed +sh exercises/26_regex.sh 09 > /dev/null 2>&1 || failed +sh exercises/26_regex.sh 10 > /dev/null 2>&1 || failed +sh exercises/26_regex.sh 99999 > /dev/null 2>&1 && failed +sh exercises/26_regex.sh loop > /dev/null 2>&1 || failed +sh exercises/26_regex.sh quiz > /dev/null 2>&1 || failed +sh exercises/26_regex.sh quiz.sh > /dev/null 2>&1 && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/27_regex2.sh b/tests/27_regex2.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/27_regex2.sh billybob@bob1.com || failed +sh exercises/27_regex2.sh David2@test.xyz || failed +sh exercises/27_regex2.sh 1@t.co || failed + +sh exercises/27_regex2.sh @gmail.com && failed +sh exercises/27_regex2.sh billybob@bob2.c2 && failed +sh exercises/27_regex2.sh billybob@.c2 && failed +sh exercises/27_regex2.sh billybob@mail.COM && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/28_case.sh b/tests/28_case.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +printf "scissors\n" | sh exercises/28_case.sh | grep -Eq "tie|lost|won" || failed +printf "scissor\n" | sh exercises/28_case.sh | grep -Eq "tie|lost|won" && failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/29_gzip.sh b/tests/29_gzip.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup() { + [ -f "LICENSE.gz" ] && rm LICENSE.gz +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/29_gzip.sh || failed + +[ -s LICENSE.gz ] || failed +[ -s LICENSE ] || failed + +leis_size=$(stat -c%s LICENSE) +gz_size=$(stat -c%s LICENSE.gz) + +[ $leis_size -gt $gz_size ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/30_tar.sh b/tests/30_tar.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup() { + [ -f "shelllings.tar.gz" ] && rm shelllings.tar.gz +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/30_tar.sh || failed + +[ -s shelllings.tar.gz ] || failed + +file_size=$(du -s shelllings.tar.gz | awk '{print $1}') + +[ $file_size -lt 20 ] || failed +[ $file_size -gt 10 ] || failed + +printf "${GREEN}Passed${RESET}\n" diff --git a/tests/31_trap.sh b/tests/31_trap.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -eu +RED="\033[31m" +GREEN="\033[32m" +RESET="\033[0m" + +cleanup() { + [ -f "file.txt" ] && rm -f file.txt +} +trap cleanup EXIT + +failed() { + printf "${RED}Failed${RESET}\n" + exit 1 +} + +sh exercises/31_trap.sh | grep -Eq "Removing..." || failed + +grep 'trap' exercises/31_trap.sh | grep "rm file\.txt" | grep 'Removing...' | grep -q 'EXIT' || failed + +[ -f "file.txt" ] && failed + +printf "${GREEN}Passed${RESET}\n"