├── .gitignore ├── README.md ├── activities ├── 1.1.1.shells.sh ├── 1.2.1.variables.sh ├── 1.3.1.globs.sh └── 1.4.1.functions.sh ├── code ├── 1.1.bash_core.sh ├── 1.2.variables.sh ├── 1.3.globbing.sh ├── 1.4.pipes_redirects.sh ├── 2.1.command_substitution.sh ├── 2.2.functions.sh ├── 2.3.exit_codes.sh ├── 2.4.tests.sh ├── 2.5.loops.sh ├── 3.1.scripts_and_startup.sh ├── 3.2.set.sh ├── 3.3.subshells.sh ├── 3.4.ifs.sh ├── 4.1.traps_and_jobs.sh ├── 4.2.process_substitution.sh ├── 4.4.cheapci.sh └── commands_used.txt ├── exercises ├── README.md ├── exercises_1.txt ├── exercises_2_loops_tests_functions_exit_codes.txt ├── exercises_3_scripts.txt ├── exercises_4_subshells_and_ifs.txt └── exercises_5_traps_strings_autocomplete.txt └── slides ├── .gitignore ├── diagrams ├── pipes_and_redirect_pipe.png ├── pipes_and_redirect_pipe.xml ├── pipes_and_redirect_redirect ├── pipes_and_redirect_redirect.png ├── pipes_and_redirect_redirect.xml ├── pipes_and_redirects_basic ├── pipes_and_redirects_basic. └── pipes_and_redirects_basic.png ├── images ├── README ├── bash_startup.png ├── lbthw.png ├── me.jpg └── shell_history.png ├── introduction_to_bash.pdf └── introduction_to_bash.pptx /.gitignore: -------------------------------------------------------------------------------- 1 | **~* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Bash 2 | 3 | ## Pre-Requisites 4 | 5 | ### Bash Shell 6 | 7 | You must be using a bash shell. 8 | 9 | Follow this to find out what shell you are in: 10 | 11 | https://www.cyberciti.biz/tips/how-do-i-find-out-what-shell-im-using.html 12 | 13 | ### Bash Shell Version 14 | 15 | To accurately follow the course, you will need Bash version 4+. If you have a lower version, then not everything will work as expected, but you can still follow the course. 16 | 17 | You can find out your version by typing: `echo $BASH_VERSION` in your terminal. 18 | 19 | If you get no output, you may not be in bash at all (see above). 20 | 21 | If you are using a Mac, then by default you will have version 3.x. See [here](https://apple.stackexchange.com/questions/193411/update-bash-to-version-4-0-on-osx) for advice on upgrading. 22 | 23 | ### Basic Shell Utility Knowledge 24 | 25 | You don't neeed much shell utility knowledge to follow the course, but if you are unsure whether you have the basic proficiency required I recommend following [this short course](https://learnpythonthehardway.org/book/appendixa.html). 26 | 27 | 28 | ## Layout of this repository 29 | 30 | `exercises` - course exercises 31 | 32 | `code` - the example code in scripts 33 | 34 | `slides` - course slides 35 | 36 | For author only: See `lbthw/oreilly_course/*` 37 | 38 | ## Links 39 | 40 | Some useful links that may be mentioned in the course: 41 | 42 | [Which bash shell version am I using?](https://www.cyberciti.biz/tips/how-do-i-find-out-what-shell-im-using.html) 43 | 44 | [Bash version update guidance](https://apple.stackexchange.com/questions/193411/update-bash-to-version-4-0-on-osx) 45 | 46 | [Crash course in the command line](https://learnpythonthehardway.org/book/appendixa.html) 47 | 48 | [CheapCI - a complete and useful program in bash](https://github.com/ianmiell/cheapci) 49 | 50 | [ShellCheck - get advice on your bash code](http://www.shellcheck.net) 51 | 52 | [Differences between zsh and bash](https://www.quora.com/What-is-the-difference-between-bash-and-zsh) 53 | 54 | [Grep](https://en.wikipedia.org/wiki/Grep) 55 | 56 | [bashtop](https://github.com/aristocratos/bashtop/blob/master/bashtop) 57 | -------------------------------------------------------------------------------- /activities/1.1.1.shells.sh: -------------------------------------------------------------------------------- 1 | csh 2 | echo $dirstack 3 | exit 4 | -------------------------------------------------------------------------------- /activities/1.2.1.variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Variables and quoting 4 | ####################### 5 | # Simple variable 6 | MYSTRING=astring 7 | echo $MYSTRING 8 | # Quoting 9 | MYSENTENCE=A sentence 10 | MYSENTENCE="A sentence" 11 | echo $MYSENTENCE 12 | # Single vs double quotes 13 | MYSENTENCE="A sentence with $MYSTRING in it" 14 | echo $MYSENTENCE 15 | MYSENTENCE='A sentence with $MYSTRING in it' 16 | echo $MYSENTENCE 17 | 18 | # Shell variables 19 | ################# 20 | echo $PPID 21 | PPID=nonsense 22 | echo $PPID 23 | # Readonly variables 24 | readonly MYVAR=astring 25 | MYVAR=anotherstring 26 | 27 | 28 | # exported variables 29 | #################### 30 | MYSTRING=astring 31 | bash 32 | echo $MYSTRING 33 | exit 34 | echo $MYSTRING 35 | unset MYSTRING 36 | echo $MYSTRING 37 | export MYSTRING=anotherstring 38 | bash 39 | echo $MYSTRING 40 | exit 41 | 42 | 43 | # env / export 44 | ############## 45 | # env 46 | env 47 | # There is also export 48 | export 49 | 50 | # Arrays 51 | ######## 52 | # Version string 53 | echo $BASH_VERSION 54 | # Version number 55 | echo $BASH_VERSINFO 56 | # I heard it's an array... 57 | echo $BASH_VERSINFO[0] 58 | # Hmm, I need to use curlies to tell bash it's an array. 59 | echo ${BASH_VERSINFO[0]} 60 | # Curlies are good practice 61 | echo ${BASH_VERSINFO} 62 | # Compare this BASH_VERSION, not BASH_VERSINFO 63 | echo $BASH_VERSION_and_some_string 64 | # to this 65 | echo ${BASH_VERSION}_and_some_string 66 | # a string is actually the zeroth element of the bash array. 67 | echo ${PPID} 68 | echo ${PPID[0]} 69 | echo ${PPID[1]} 70 | # Print each element... 71 | echo ${BASH_VERSINFO[1]} 72 | echo ${BASH_VERSINFO[2]} 73 | echo ${BASH_VERSINFO[3]} 74 | echo ${BASH_VERSINFO[4]} 75 | echo ${BASH_VERSINFO[5]} 76 | # Until item is empty... 77 | echo ${BASH_VERSINFO[6]} 78 | # Print the whole array 79 | echo ${BASH_VERSINFO[@]} 80 | 81 | # To create an array 82 | A=(a b c) 83 | echo ${A[1]} 84 | 85 | # Associative arrays 86 | # Version - if < 4... https://clubmate.fi/upgrade-to-bash-4-in-mac-os-x/ 87 | bash --version 88 | declare -A MYAA=([one]=1 [two]=2 [three]=3) 89 | MYAA[one]="1" 90 | MYAA[two]="2" 91 | echo $MYAA 92 | echo ${MYAA[one]} 93 | MYAA[one]="1" 94 | WANT=two 95 | echo ${MYAA[$WANT]} 96 | -------------------------------------------------------------------------------- /activities/1.3.1.globs.sh: -------------------------------------------------------------------------------- 1 | # Make folder 2 | mkdir lbthw_glob 3 | cd lbthw_glob 4 | 5 | # Create some files 6 | touch file1 file2 file3 file10 file11 7 | 8 | # Simplest glob 9 | ###################### 10 | # * 11 | ls * 12 | # Not just ls - any command 13 | echo * 14 | 15 | # Quoting Globs 16 | ####################### 17 | # Simple assignment 18 | MYGLOB=* 19 | echo $MYGLOB 20 | # Quoting protects globs 21 | MYGLOB="*" 22 | # Single or double quotes 23 | echo "$MYGLOB" 24 | MYGLOB='*' 25 | # Unlike variables 26 | echo "$MYGLOB" 27 | echo '$MYGLOB' 28 | echo $MYGLOB 29 | 30 | # Other Glob characters 31 | ####################### 32 | # ? 33 | ls file? 34 | ls file* 35 | 36 | # [n] 37 | ls file[1] 38 | 39 | # [n-m] 40 | ls file[1-3] 41 | ls f*[1-3]1 42 | man ascii 43 | 44 | # dotfiles 45 | ####################### 46 | touch .adotfile 47 | mkdir .adotdir 48 | ls * 49 | echo * 50 | echo 'Run ls .*' 51 | ls .* 52 | echo 'Run .*' 53 | echo .* 54 | echo 'Run .a*' 55 | echo .a* 56 | ls . 57 | ls .. 58 | 59 | 60 | # vs regexp 61 | rename -n 's/(.*)/new$1/' * 62 | ls 63 | 64 | # Cleanup 65 | cd .. 66 | rm -rf lbthw_glob 67 | -------------------------------------------------------------------------------- /activities/1.4.1.functions.sh: -------------------------------------------------------------------------------- 1 | # Basic functions 2 | function myfunc { 3 | echo hello world 4 | } 5 | myfunc 6 | 7 | # Function arguments 8 | function myfunc { 9 | echo arg1: $1 10 | echo arg2: $2 11 | } 12 | myfunc "hello world" 13 | myfunc hello world 14 | 15 | # Functions and set/unset variables 16 | function myfunc { 17 | echo $myvar 18 | } 19 | myfunc 20 | myvar=hello 21 | myfunc 22 | 23 | # local variables 24 | function myfunc { 25 | local myvar=inside 26 | echo $myvar 27 | } 28 | myfunc 29 | myvar=outside 30 | echo $myvar 31 | myfunc 32 | 33 | # locals outside functions 34 | local newvar=newval 35 | 36 | -------------------------------------------------------------------------------- /code/1.1.bash_core.sh: -------------------------------------------------------------------------------- 1 | tcsh 2 | echo $dirstack 3 | exit 4 | echo $dirstack 5 | echo $$ 6 | bash 7 | echo $$ 8 | exit 9 | echo $$ 10 | man ls 11 | zsh 12 | ls TAB 13 | ls *TAB 14 | -------------------------------------------------------------------------------- /code/1.2.variables.sh: -------------------------------------------------------------------------------- 1 | MYSTRING=astring 2 | echo $MYSTRING 3 | MYSENTENCE=A sentence 4 | MYSENTENCE="A sentence" 5 | echo $MYSENTENCE 6 | MYSENTENCE="A sentence with $MYSTRING in it" 7 | echo $MYSENTENCE 8 | MYSENTENCE='A sentence with $MYSTRING in it' 9 | echo $MYSENTENCE 10 | echo $PPID 11 | PPID=nonsense 12 | echo $PPID 13 | readonly READONLYVAR=astring 14 | READONLYMYVAR=anotherstring 15 | MYSTRING=astring 16 | bash 17 | echo $MYSTRING 18 | exit 19 | echo $MYSTRING 20 | unset MYSTRING 21 | echo $MYSTRING 22 | export MYSTRING=anotherstring 23 | bash 24 | echo $MYSTRING 25 | exit 26 | env 27 | declare 28 | declare -f # just functions 29 | declare -F # just function headers 30 | declare -r # readonlys 31 | declare -i # just integers 32 | declare -v 33 | bash --version 34 | declare -a 35 | echo $BASH_VERSINFO 36 | echo $BASH_VERSINFO[0] 37 | echo ${BASH_VERSINFO[0]} 38 | echo $BASH_VERSION_and_some_string 39 | echo ${BASH_VERSION}_and_some_string 40 | echo ${BASH_VERSINFO} 41 | unset A 42 | echo $A 43 | A=1 44 | echo $A 45 | echo ${A[0]} 46 | echo ${A[1]} 47 | echo ${BASH_VERSINFO[0]} 48 | echo ${BASH_VERSINFO[1]} 49 | echo ${BASH_VERSINFO[2]} 50 | echo ${BASH_VERSINFO[3]} 51 | echo ${BASH_VERSINFO[4]} 52 | echo ${BASH_VERSINFO[5]} 53 | echo ${BASH_VERSINFO[6]} 54 | -------------------------------------------------------------------------------- /code/1.3.globbing.sh: -------------------------------------------------------------------------------- 1 | mkdir itb_glob 2 | cd itb_glob 3 | touch file1 file2 file3 4 | ls * 5 | echo * 6 | echo ls * 7 | echo ls '*' 8 | echo '$HOME' 9 | echo "$HOME" 10 | ls "*" 11 | ls *1 12 | ls file[12] 13 | ls file[a-z] 14 | ls fil[a-z][0-9] 15 | echo file? 16 | touch .adotfile 17 | mkdir .adotfolder 18 | touch .adotfolder/file1 .adotfolder/.adotfile 19 | ls 20 | ls * 21 | echo .* 22 | ls .* 23 | cd . 24 | echo text > afile 25 | grep text afile 26 | grep '*' afile 27 | grep '.*' afile 28 | -------------------------------------------------------------------------------- /code/1.4.pipes_redirects.sh: -------------------------------------------------------------------------------- 1 | mkdir itb_pipes_redirects 2 | cd itb_pipes_redirects 3 | echo "contents of file1" > file1 4 | cat file1 5 | cat file1 | grep -c file 6 | cat file1 | grep 'contents of file1' 7 | cat file1 | grep 'not in file' 8 | cat file2 9 | cat file2 | grep -c file 10 | echo asd > /dev/stdout 11 | ls -l /dev/stdout 12 | cat file2 | grep -c file 13 | command_does_not_exist 14 | command_does_not_exist 2> /dev/null 15 | grep -c file < file1 16 | echo line1 > file3 17 | echo line2 > file3 18 | echo line3 >> file3 19 | cat file3 20 | -------------------------------------------------------------------------------- /code/2.1.command_substitution.sh: -------------------------------------------------------------------------------- 1 | hostname 2 | echo "My hostname is: $(hostname)" 3 | echo 'My hostname is: $(hostname)' 4 | echo "My hostname is: `hostname`" 5 | echo "My hostname is: $(hostname)" 6 | cd itb_cmdsub 7 | ls $(echo a $(echo b)) 8 | cd .. 9 | ls `echo a `echo b`` 10 | ls `echo a \`echo b\`` 11 | echo `echo \`echo \\\`echo inside\\\`\`` 12 | echo $(echo $(echo $(echo inside))) 13 | -------------------------------------------------------------------------------- /code/2.2.functions.sh: -------------------------------------------------------------------------------- 1 | function myfunc { 2 | echo Hello World 3 | } 4 | myfunc 5 | function myfunc { 6 | echo $1 7 | echo $2 8 | } 9 | myfunc "Hello World" 10 | myfunc Hello World 11 | function myfunc { 12 | echo $myvar 13 | } 14 | myfunc 15 | myvar="Hi from outside the function" 16 | myfunc 17 | function myfunc { 18 | local myvar="Hi from inside the function" 19 | echo $myvar 20 | } 21 | myfunc 22 | echo $myvar 23 | local myvar="Will this work?" 24 | builtin ls 25 | builtin cd /tmp 26 | builtin cd - 27 | type cd 28 | type ls 29 | function cd() { 30 | echo 'No!' 31 | } 32 | cd /tmp 33 | builtin cd /tmp 34 | type cd 35 | cd - 36 | unset -f cd 37 | cd /tmp 38 | type cd 39 | cd - 40 | declare -f 41 | declare -F 42 | type builtin 43 | help builtin 44 | builtin grep 45 | builtin notaprogram 46 | which grep 47 | which cd 48 | which builtin 49 | which doesnotexist 50 | type which 51 | alias cd=doesnotexist 52 | alias 53 | cd 54 | unalias cd 55 | cd /tmp 56 | cd - 57 | alias 58 | -------------------------------------------------------------------------------- /code/2.3.exit_codes.sh: -------------------------------------------------------------------------------- 1 | ls 2 | echo $? 3 | doesnotexist 4 | echo $? 5 | bash 6 | function trycmd { 7 | $1 8 | if [[ $? -eq 127 ]] 9 | then 10 | echo 'What are you doing?' 11 | fi 12 | } 13 | trycmd ls 14 | trycmd doesnotexist 15 | exit 16 | echo 'grepme' > afile.txt 17 | grep not_there afile.txt 18 | echo $? 19 | if grep grepme afile.txt 20 | then 21 | echo 'matched!' 22 | fi 23 | bash 24 | exit 67 25 | echo $? 26 | bash 27 | function trycmd { 28 | $1 29 | if [[ $? -eq 127 ]] 30 | then 31 | echo 'What are you doing?' 32 | return 1 33 | fi 34 | } 35 | trycmd ls 36 | trycmd doesnotexit 37 | exit 38 | return 1 39 | echo $$ 40 | ps -ef | grep bash | grep $$ 41 | echo $- 42 | if grep not_there /dev/null 43 | then 44 | echo there 45 | else 46 | echo not there 47 | fi 48 | -------------------------------------------------------------------------------- /code/2.4.tests.sh: -------------------------------------------------------------------------------- 1 | [ 1 = 0 ] 2 | echo $? 3 | [ 1 = 1 ] 4 | echo $? 5 | [ 0 == 0 ] 6 | echo $? 7 | [ 1 == 1 ] 8 | echo $? 9 | A=1 10 | [ $A = 1 ] 11 | echo $? 12 | [ $A = 2 ] 13 | echo $? 14 | [ $(hostname) = myhost ] 15 | echo $? 16 | builtin [ 17 | which [ 18 | builtin [ ] 19 | which test 20 | man test 21 | man [ 22 | test 1 == 1 23 | echo $? 24 | doesnotexist && echo here 25 | doesnotexist || echo here 26 | mkdir tmp && cd tmp 27 | ([ 1 = 1 ] || [ ! 0 = 0 ]) && [ 2 = 2 ] 28 | echo $? 29 | [ 1 = 1 -o ! 0 = 0 -a 2 = 2 ] 30 | echo $? 31 | [[ 1 = 1 ]] 32 | echo $? 33 | unset DOESNOTEXIST 34 | [ ${DOESNOTEXIST} = '' ] 35 | echo $? 36 | [ = '' ] 37 | [[ ${DOESNOTEXIST} = '' ]] 38 | echo $? 39 | [ x${DOESNOTEXIST} = x ] 40 | echo $? 41 | [ '' = '' ] 42 | [ x = 'x' ] 43 | [[ "x$DOESNOTEXIST" = "x" ]] 44 | echo $PWD 45 | [ -z "$PWD" ] 46 | echo $? 47 | unset DOESNOTEXIST 48 | [ -z "$DOESNOTEXIST" ] 49 | echo $? 50 | [ -z ] 51 | echo $? 52 | mkdir itb_tests_dir 53 | cd itb_tests_dir 54 | touch itb_tests_file 55 | [ -a itb_tests_file ] 56 | echo $? 57 | [ -d itb_tests_file ] 58 | echo $? 59 | [ -a itb_tests_dir ] 60 | echo $? 61 | [ -d itb_tests_dir ] 62 | echo $? 63 | cd - 64 | rm -rf itb_tests_dir 65 | [[ 10 < 2 ]] 66 | echo $? 67 | [[ '10' < '2' ]] 68 | echo $? 69 | [ 10 -lt 2 ] 70 | echo $? 71 | [ 1 -lt 2 ] 72 | echo $? 73 | [ 10 -gt 1 ] 74 | echo $? 75 | [ 1 -eq 1 ] 76 | echo $? 77 | [ 1 -ne 1 ] 78 | echo $? 79 | [ '10' -lt '2' ] 80 | echo $? 81 | declare -i 82 | if [[ 10 -lt 2 ]] 83 | then 84 | echo 'right' 85 | elif [[ 10 -gt 2 ]] 86 | then 87 | echo 'right' 88 | else 89 | echo 'impossible' 90 | fi 91 | if [[ 10 -lt 2 ]]; then echo 'impossible'; fi 92 | -------------------------------------------------------------------------------- /code/2.5.loops.sh: -------------------------------------------------------------------------------- 1 | mkdir itb_loops && cd itb_loops 2 | for (( i=0; i < 20; i++ )) 3 | do 4 | echo $i 5 | echo $i > file${i}.txt 6 | done 7 | ls 8 | for f in $(ls *txt) 9 | do 10 | echo "File $f contains: $(cat $f)" 11 | done 12 | for f in file1.txt file2.txt file3.txt 13 | do 14 | echo "File $f contains: $(cat $f)" 15 | done 16 | n=0 17 | while [[ ! -a newfile ]] 18 | do 19 | ((n++)) 20 | echo "In iteration $n" 21 | if [[ $(cat file${n}.txt) == "15" ]] 22 | then 23 | touch newfile 24 | fi 25 | done 26 | a=1 27 | case "$a" in 28 | 1) echo 'a is 1'; echo 'ok';; 29 | 2) echo 'a is 2'; echo 'ok';; 30 | *) echo 'a is unmatched'; echo 'failure';; 31 | esac 32 | ls -l 33 | #!/bin/bash 34 | while getopts "ab:c" opt 35 | do 36 | case "$opt" in 37 | a) echo '-a invoked';; 38 | b) echo "-b invoked with argument: ${OPTARG}";; 39 | c) echo '-c invoked';; 40 | esac 41 | done 42 | chmod +x case.sh 43 | ./case.sh -a 44 | ./case.sh -b 45 | ./case.sh -b "an argument" 46 | ./case.sh -a -b -c 47 | ./case.sh 48 | -------------------------------------------------------------------------------- /code/3.1.scripts_and_startup.sh: -------------------------------------------------------------------------------- 1 | mkdir -p itb_scripts_and_startups 2 | cd itb_scripts_and_startups 3 | #!/bin/bash 4 | echo I am a script 5 | ./simple_script 6 | mkdir tmp 7 | cd tmp 8 | ../simple_script 9 | cd .. 10 | rm -rf tmp 11 | chmod +x simple_script 12 | ./simple_script 13 | simple_script 14 | echo $PATH 15 | echo $PATH 16 | PATH=${PATH}:. 17 | simple_script 18 | MYVAR=Hello 19 | echo 'echo $MYVAR' > simple_echo 20 | chmod +x simple_echo 21 | ./simple_echo 22 | source simple_echo 23 | unset MYVAR 24 | echo $MYVAR 25 | echo 'MYVAR=1' > simple_echo 26 | ./simple_echo 27 | echo $MYVAR 28 | . simple_echo 29 | echo $MYVAR 30 | env -i bash --noprofile --norc 31 | env 32 | -------------------------------------------------------------------------------- /code/3.2.set.sh: -------------------------------------------------------------------------------- 1 | function a { 2 | echo in a 3 | } 4 | a 5 | set 6 | man bash 7 | set -o posix 8 | set 9 | set +o posix 10 | set 11 | set -o 12 | set 13 | env 14 | set -o errexit 15 | set -o xtrace 16 | set -o nounset 17 | #!/bin/bash 18 | set -o errexit 19 | set -o xtrace 20 | set -o nounset 21 | pwd 22 | cd $HOME 23 | cd - 24 | echo $DOESNOTEXIST 25 | echo "should not get here" 26 | chmod +x ascript.sh 27 | ./ascript.sh 28 | set -e 29 | set -x 30 | set -o errexit 31 | set -o xtrace 32 | #!/bin/bash 33 | set -o nounset 34 | set -o xtrace 35 | A="some value" 36 | PS4='$(date "+%s%N => ")' 37 | B= 38 | echo "${A}" 39 | A="another value" 40 | echo "${A}" 41 | echo "${B}" 42 | pwd 43 | sleep 1 44 | pwd 45 | shopt -s globstar 46 | ls ** 47 | #!/bin/bash 48 | A=some value 49 | echo "${A} 50 | echo "${B}" 51 | bash -n debug_script.sh 52 | bash debug_script.sh 53 | bash -v debug_script.sh 54 | bash -x debug_script.sh 55 | shellcheck ~/.bashrc 56 | -------------------------------------------------------------------------------- /code/3.3.subshells.sh: -------------------------------------------------------------------------------- 1 | mkdir itb_subshells && cd lbthw_subshells 2 | VAR1='the original variable' 3 | ( 4 | echo Inside the subshell 5 | echo ${VAR1} 6 | VAR1='the updated variable' 7 | echo ${VAR1} 8 | VAR2='the second variable' 9 | echo ${VAR2} 10 | ) 11 | echo $VAR1 12 | echo $VAR2 13 | $ echo ${VAR1} 14 | ( 15 | export VAR1='the first variable exported' 16 | ) 17 | echo ${VAR1} 18 | echo Some output, ls to follow >> logfile 19 | ls >> logfile 20 | ( 21 | echo Some output, ls to follow 22 | ls 23 | ) >> logfile 24 | pwd 25 | ( mkdir itb_subshells_2 26 | cd itb_subshells_2 27 | pwd ) 28 | pwd 29 | mkdir itb_subshells_3 30 | cd itb_subshells_3 31 | mkdir a b c 32 | for f in $(ls -d) 33 | do 34 | ( 35 | cd $f 36 | echo creating file in $f 37 | touch newfile 38 | ) 39 | done 40 | -------------------------------------------------------------------------------- /code/3.4.ifs.sh: -------------------------------------------------------------------------------- 1 | mkdir itb_ifs && cd lbthw_ifs 2 | echo file1 created > "Spaces in filename1.txt" 3 | cat "Spaces in filename1.txt" 4 | echo file2 created > "Spaces in filename2.txt" 5 | cat "Spaces in filename2.txt" 6 | for f in $(ls) 7 | do 8 | ls $f 9 | done 10 | for f in $(echo 1 2 3 4) 11 | do 12 | echo $f 13 | done 14 | echo $IFS 15 | set | grep IFS 16 | IFS=$' \t\n' 17 | Spaces in filename1.txt 18 | Spaces in filename2.txt 19 | Spaces 20 | in 21 | filename1.txt 22 | Spaces 23 | in 24 | filename2.txt 25 | IFS=$'\t\n' 26 | for f in $(ls) 27 | do 28 | ls $f 29 | done 30 | find . -type f | xargs -n1 grep somestring 31 | find . -type f -print0 | xargs -0 -n1 grep somestring 32 | IFS=$'\n\t' 33 | for f in $(echo 1 2 3 4) 34 | do 35 | echo $f 36 | done 37 | for f in 1 2 3 4 38 | do 39 | echo $f 40 | done 41 | IFS=$' \n\t' 42 | for f in "1 2" 3 4 43 | do 44 | echo $f 45 | done 46 | -------------------------------------------------------------------------------- /code/4.1.traps_and_jobs.sh: -------------------------------------------------------------------------------- 1 | sleep 40 & 2 | [1] 39165 3 | pwd 4 | pwd 5 | [1]+ Done sleep 40 6 | sleep 10 & 7 | wait 8 | [1]+ Done sleep 10 9 | sleep 999 # NOW HIT CTRL, HOLD IT DOWN AND THEN HIT C (CTRL-c) 10 | echo $? 11 | man signal 12 | sleep 999 # NOW HIT CTRL, HOLD IT DOWN AND THEN HIT Z (CTRL-z) 13 | echo $? 14 | jobs 15 | kill %1 16 | sleep 999 & 17 | KILLPID=${!} 18 | echo ${KILLPID} 19 | kill -15 ${KILLPID} 20 | echo $? 21 | echo ${?} 22 | : 23 | : you can type anything here 24 | echo $? 25 | cd /tmp && : now in tmp folder && cd - && : now back in previous folder 26 | while :; do sleep 5; done 27 | mkdir -p itb_traps && cd lbthw_traps 28 | vi trap_exit.sh 29 | #!/bin/bash 30 | trap "echo trapped" INT 31 | while :; do sleep 5; done 32 | chmod +x trap_exit.sh 33 | ./trap_exit.sh # NOW HIT CTRL-c 34 | # HIT CTRL-z 35 | kill %1 36 | #!/bin/bash 37 | trap "echo trapped" EXIT 38 | sleep 999 & 39 | wait 40 | ./trap_exit.sh & 41 | TRAP_EXIT_PID=$! 42 | kill -15 ${TRAP_EXIT_PID} 43 | ps -ef | grep sleep 44 | ./trap_exit.sh & 45 | TRAP_EXIT_PID=$(echo ${!}) 46 | kill -9 ${TRAP_EXIT_PID} 47 | sleep 120 & 48 | sleep 121 & 49 | jobs 50 | [1]- Running sleep 120 & 51 | [2]+ Running sleep 121 & 52 | fg 53 | sleep 121 54 | [2]+ Stopped sleep 121 55 | bg 56 | [2]+ sleep 121 & 57 | kill %1 58 | RETURN 59 | [1]+ Terminated: 15 sleep 120 60 | -------------------------------------------------------------------------------- /code/4.2.process_substitution.sh: -------------------------------------------------------------------------------- 1 | mkdir itb_process_subst && cd itb_process_subst 2 | mkdir a 3 | mkdir b 4 | touch a/1 a/2 5 | touch b/2 b/3 6 | ls a 7 | ls b 8 | ls a 9 | ls b 10 | ls a > aout 11 | ls b > bout 12 | diff aout bout 13 | rm aout bout 14 | diff <(ls a) <(ls b) 15 | diff <(ls a) <(ls b) 16 | -------------------------------------------------------------------------------- /code/4.4.cheapci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple CI 4 | 5 | set -o pipefail 6 | set -o nounset 7 | 8 | # Defaults 9 | FORCE=0 10 | VERBOSE=0 11 | REPO="" 12 | EMAIL="" 13 | NAME="ci" 14 | TEST_DIR="." 15 | TEST_COMMAND="./test.sh" 16 | MAIL_CMD="mail" 17 | MAIL_CMD_ATTACH_FLAG="-A" 18 | MAIL_CMD_RECIPIENTS_FLAG="-t" 19 | MAIL_CMD_SUBJECT_FLAG="-s" 20 | PRE_SCRIPT="/bin/true" 21 | POST_SCRIPT="/bin/true" 22 | TIMEOUT_S=86400 23 | 24 | PAGER=${PAGER:-more} 25 | 26 | function show_help() { 27 | cat > /dev/stdout << END 28 | ${0} -r -l [-q ] [-w ] 29 | [-m ] [-a ] [-t ] 30 | [-s ] [-n name] [-d ] 31 | [-c ] [-f] [-v] [-h] 32 | 33 | REQUIRED ARGS: 34 | -r - git repository, eg https://github.com/myname/myproj (required) 35 | -l - local checkout of code (that gets updated to determine whether a run is needed) (required) 36 | 37 | OPTIONAL ARGS: 38 | -q - script to run just before actually performing test (default ${PRE_SCRIPT}) 39 | -w - script to run just after actually performing test (default ${POST_SCRIPT}) 40 | -m - email address to send to using "mail" command (default logs to stdout) 41 | -a - mail command to use (default=${MAIL_CMD}) 42 | -n - name for ci (unique, must be a valid directory name), eg myproj (default=${NAME}) 43 | -d - directory within repository to navigate to (default=${TEST_DIR}) 44 | -c - test command to run from -d directory (default=${TEST_COMMAND}) 45 | -t - attach argument flag for mail command (default=${MAIL_CMD_ATTACH_FLAG}, empty string means no-attach) 46 | -s - subject flag for mail command (default=${MAIL_CMD_RECIPIENTS_FLAG}) 47 | -e - recipients flag (default=${MAIL_CMD_RECIPIENTS_FLAG}, empty string means no flag needed) 48 | -f - force a run even if repo has no updates (default off) 49 | -v - verbose logging (default off) 50 | -i - timeout in seconds (default 86400, ie one day, does KILL one hour after that) 51 | -h - show help 52 | 53 | EXAMPLES 54 | 55 | - "Clone -r https://github.com/ianmiell/shutit.git if a git pull on /space/git/shutit indicates there's been an update. 56 | Then navigate to test, run ./test.sh and mail ian.miell@gmail.com if there are any issues" 57 | 58 | ./cheapci -r https://github.com/ianmiell/shutit.git -l /space/git/shutit -d test -c ./test.sh -m ian.miell@gmail.com 59 | 60 | 61 | - "Run this continuously in a crontab." 62 | 63 | Crontab line: 64 | 65 | * * * * * cd /path/to/cheapci && ./cheapci -r https://github.com/ianmiell/shutit.git -l /space/git/shutit -d test -c ./test.sh -m ian.miell@gmail.com 66 | END 67 | } 68 | 69 | 70 | while getopts "h?vfm:n:d:r:l:c:a:q:w:t:e:s:" opt 71 | do 72 | case "${opt}" in 73 | h|\?) 74 | show_help 75 | exit 0 76 | ;; 77 | v) VERBOSE=1 ;; 78 | f) FORCE=1 ;; 79 | r) REPO=${OPTARG} ;; 80 | m) EMAIL=${OPTARG} ;; 81 | n) NAME=${OPTARG} ;; 82 | d) TEST_DIR=${OPTARG} ;; 83 | l) LOCAL_CHECKOUT=${OPTARG} ;; 84 | c) TEST_COMMAND=${OPTARG} ;; 85 | q) PRE_SCRIPT=${OPTARG} ;; 86 | w) POST_SCRIPT=${OPTARG} ;; 87 | a) MAIL_CMD=${OPTARG} ;; 88 | t) MAIL_CMD_ATTACH_FLAG=${OPTARG} ;; 89 | e) MAIL_CMD_RECIPIENTS_FLAG=${OPTARG} ;; 90 | s) MAIL_CMD_SUBJECT_FLAG=${OPTARG} ;; 91 | i) TIMEOUT_S=${OPTARG} ;; 92 | esac 93 | done 94 | 95 | shift "$((OPTIND-1))" 96 | 97 | if [[ ${REPO} = "" ]] 98 | then 99 | show_help 100 | exit 1 101 | fi 102 | 103 | 104 | # To force a run even if no updates. 105 | 106 | if [[ ${VERBOSE} -gt 0 ]] 107 | then 108 | set -x 109 | fi 110 | 111 | BUILD_DIR_BASE="/tmp/${NAME}" 112 | BUILD_DIR="${BUILD_DIR_BASE}/${NAME}_builddir" 113 | mkdir -p "${BUILD_DIR}" 114 | LOG_FILE="${BUILD_DIR}/${NAME}_build_${RANDOM}.log.txt" 115 | BUILD_LOG_FILE="${BUILD_DIR}/${NAME}_build.log.txt" 116 | LOCK_FILE="${BUILD_DIR}/${NAME}_ci.lck" 117 | 118 | function cleanup() { 119 | rm -rf "${BUILD_DIR}" 120 | rm -f "${LOCK_FILE}" 121 | # get rid of /tmp detritus, leaving anything accessed 2 days ago+ 122 | find "${BUILD_DIR_BASE}"/* -type d -atime +1 -exec rm {} -rf + 123 | echo "cleanup done" 124 | } 125 | 126 | function send_mail() { 127 | msg=${1} 128 | if [[ ${LOG_FILE} != "" ]] && [[ ${MAIL_CMD_ATTACH_FLAG} != "" ]] 129 | then 130 | log_file_arg=(${MAIL_CMD_ATTACH_FLAG} ${LOG_FILE}) 131 | fi 132 | if [[ ${EMAIL} != "" ]] && [[ ${MAIL_CMD} != "" ]] 133 | then 134 | echo "${msg}" | ${MAIL_CMD} "${MAIL_CMD_SUBJECT_FLAG}" "${msg}" "${log_file_arg[@]}" "${MAIL_CMD_RECIPIENTS_FLAG}" "${EMAIL}" 135 | else 136 | echo "${msg}" 137 | fi 138 | } 139 | 140 | date 2>&1 | tee -a "${BUILD_LOG_FILE}" 141 | 142 | # Lockfile 143 | if [[ -a ${LOCK_FILE} ]] 144 | then 145 | echo "Already running" | tee -a "${BUILD_LOG_FILE}" 146 | exit 147 | fi 148 | 149 | trap cleanup TERM INT QUIT EXIT 150 | 151 | touch "${LOCK_FILE}" 152 | # Fetch changes 153 | pushd "${LOCAL_CHECKOUT}" 154 | git fetch origin master 2>&1 | tee -a "${BUILD_LOG_FILE}" 155 | # See if there are any incoming changes 156 | updates=$(git log HEAD..origin/master --oneline | wc -l) 157 | echo "Updates: ${updates}" | tee -a "${BUILD_LOG_FILE}" 158 | if [[ ${updates} -gt 0 ]] || [[ ${FORCE} -gt 0 ]] 159 | then 160 | touch "${LOG_FILE}" 161 | pushd "${LOCAL_CHECKOUT}" 162 | echo "Pulling" | tee -a "${LOG_FILE}" 163 | git pull origin master 2>&1 | tee -a "${LOG_FILE}" 164 | popd 165 | # This won't exist in a bit so no point pushd'ing 166 | pushd "${BUILD_DIR}" 167 | # Clone to NAME 168 | git clone "${REPO}" "${NAME}" 169 | popd 170 | ${PRE_SCRIPT} 2>&1 | tee -a "${LOG_FILE}" 171 | EXIT_CODE="${?}" 172 | if [[ ${EXIT_CODE} -ne 0 ]] 173 | then 174 | msg="ANGRY ${NAME} on $(hostname)" 175 | fi 176 | pushd "${BUILD_DIR}"/"${NAME}"/"${TEST_DIR}" 177 | timeout "${TIMEOUT_S}" "${TEST_COMMAND}" 2>&1 | tee -a "${LOG_FILE}" 178 | EXIT_CODE=$? 179 | popd 180 | if [[ ${EXIT_CODE} -ne 0 ]] 181 | then 182 | if [[ ${EXIT_CODE} -eq 124 ]] 183 | then 184 | msg="ANGRY (TIMEOUT) ${NAME} on $(hostname)" 185 | else 186 | msg="ANGRY ${NAME} on $(hostname)" 187 | fi 188 | else 189 | msg="HAPPY ${NAME} on $(hostname)" 190 | fi 191 | ${POST_SCRIPT} 2>&1 | tee -a "${LOG_FILE}" 192 | EXIT_CODE=$? 193 | if [[ ${EXIT_CODE} -ne 0 ]] 194 | then 195 | msg="ANGRY ${NAME} on $(hostname)" 196 | fi 197 | send_mail "${msg}" 198 | fi 199 | -------------------------------------------------------------------------------- /code/commands_used.txt: -------------------------------------------------------------------------------- 1 | A list of commands and keywords used in this course 2 | =================================================== 3 | 4 | [ 5 | [[ 6 | alias 7 | bash 8 | bg 9 | builtin 10 | case 11 | cat 12 | cd 13 | chmod 14 | date 15 | declare 16 | diff 17 | do 18 | doesnotexist 19 | done 20 | echo 21 | elif 22 | else 23 | env 24 | esac 25 | exit 26 | export 27 | fg 28 | fi 29 | find 30 | for 31 | function 32 | git 33 | grep 34 | help 35 | hostname 36 | if 37 | in 38 | jobs 39 | kill 40 | local 41 | ls 42 | man 43 | mkdir 44 | popd 45 | ps 46 | pushd 47 | pwd 48 | readonly 49 | return 50 | rm 51 | set 52 | shellcheck 53 | shift 54 | shopt 55 | sleep 56 | source 57 | tcsh 58 | test 59 | then 60 | touch 61 | trap 62 | type 63 | unalias 64 | unset 65 | wait 66 | which 67 | while 68 | zsh 69 | -------------------------------------------------------------------------------- /exercises/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /exercises/exercises_1.txt: -------------------------------------------------------------------------------- 1 | Globs 2 | ===== 3 | 4 | 1) Create a folder called bash_intro_ex1 and move into it 5 | 6 | 2) Create ten files called 'one', 'two', 'three' and so on up to 'ten'. 7 | 8 | 3) Run echo to output any of the ten files that have an 'o' in the name. 9 | 10 | 4) Run echo to output any of the ten files with four characters in it. 11 | 12 | 5) Run echo to output files that begin with a vowel and have three characters 13 | 14 | 6) Create a file called '*' 15 | 16 | 7) Run ls to output only the file called '*' 17 | 18 | 8) Try and remove just the file called '*' 19 | 20 | 9) Create a folder with files with very similar names and use globs to list some and not the others at will. 21 | 22 | Variables 23 | ========= 24 | 25 | 1) Create a variable called '*', and echo that variable so that only an asterisk is shown. 26 | 27 | 2) Research the program ‘grep’. If you already know it, read the grep man page. (Type ‘man grep’). 28 | 29 | 3) Create a variable 'STILL_THERE' that will persist if you run 'bash' in your terminal and then try to echo it 30 | 31 | 32 | Stretch Tasks 33 | ============= 34 | 35 | 1) Determine the length of a string. 36 | 37 | 2) Take the output of `env` in your shell and work out why each item is there and what it might be used by. You may want to use `man bash`, or use google to figure it out. Or you could try re-setting it and see what happens. 38 | 39 | 3) Research the arrays set in your shell with `declare -a` and looking through the bash man page, and/or googling. 40 | Pipes and Redirects 41 | =================== 42 | 43 | 1) Force an error on the terminal and capture the output, working out if the error was sent to standard output or standard error. 44 | 45 | 2) Create a folder with several text files in it, and then 'cat' them to a single file. Then 'cat' them again, but append to the file so there are two copies of all files in the new file. 46 | 47 | 3) Write a command to echo standard output to file descriptor ‘3’. 48 | 49 | 4) Research what the `|&` pipe operator does. If you can't find out, try the command `cat file2 |& grep -c file` and see what changes compared to when we ran it in the course above. 50 | -------------------------------------------------------------------------------- /exercises/exercises_2_loops_tests_functions_exit_codes.txt: -------------------------------------------------------------------------------- 1 | Loops, Tests, Functions and Exit Codes 2 | ====================================== 3 | 4 | 1) Write a series of commands to check specific files and directories are in their correct place. 5 | 6 | 2) Use the `find` and `wc` commands to count the number of files on your system and perform different actions if the number is higher or lower than what you expect. 7 | 8 | 3) Write a while loop to check where a file exists every few seconds. When it does, break out of the loop with a message. 9 | 10 | 4) alias `alias`, override `cd`. Try and break things. Have fun. If you get stuck, close down your terminal, or exit your bash shell (if you haven’t overridden exit!). 11 | 12 | 5) Research all the unary operators, and try using them (see `man bash`) 13 | 14 | 6) Look up under what circumstances git returns a non-zero exit code. 15 | 16 | 7) Look up all the ‘special parameters’ and see what they do. Play with them. Research under what circumstances you might want to use them. 17 | -------------------------------------------------------------------------------- /exercises/exercises_3_scripts.txt: -------------------------------------------------------------------------------- 1 | Bash Scripting 2 | ============== 3 | 4 | 1) Look for examples of where the <() are used on github, and figure out what they're doing. Hint: search for 'process substitution' on github, and filter to shell scripts. 5 | 6 | 2) Look through your bash history (by typing `history`) and see where you could have used the <() operator. 7 | 8 | 3) Try various process substitution commands, and plug in variables and quotes to see what happens. 9 | 10 | 3) Construct a command that uses `$()` and `<()`. 11 | 12 | 4) Go through all the scripts that your bash startup went through. Read through them and try and understand what they’re doing. If you don’t understand parts of them, try and figure out what’s going on by reading `man bash`. 13 | 14 | 5) Go through the other files in the diagram that exist on your machine. 15 | 16 | 6) Read the man page to see what all the options are. Don’t worry if you don’t understand it all yet, just get a feel for what’s there. 17 | 18 | 7) Set up a shell with unique variables and functions and use set to create a script to recreate those items in another shell. 19 | 20 | 8) Find a large bash script on a social coding site such as GitHub, and run shellcheck over it. Contribute back any improvements you find. 21 | 22 | 9) Which `set` flag does nounset correspond to? 23 | -------------------------------------------------------------------------------- /exercises/exercises_4_subshells_and_ifs.txt: -------------------------------------------------------------------------------- 1 | Subshells and IFS 2 | ================= 3 | 4 | 1) Show that the group command (`{` and `}`) doesn't affect the outer shell's working directory, while a subshell (`(` and `)`)does. 5 | 6 | 2) Work out what the `BASH_SUBSHELL` variable does. 7 | 8 | 3) Find any files that have spaces in them from your '~' or $HOME folder. If you have none, create some and then find them with the find command. 9 | 10 | 4) Create a for loop that - for each file with a space in - adds a line with the filename to a file called 'spaces.txt'. 11 | -------------------------------------------------------------------------------- /exercises/exercises_5_traps_strings_autocomplete.txt: -------------------------------------------------------------------------------- 1 | Traps, Strings 2 | ============== 3 | 4 | 1) Construct an 'echo' that has a double-quoted string with a single-quoted string inside, eg one that outputs: 5 | 6 | He said 'I thought she'd said "bash was easy when $s are involved" but that can’t be true!' 7 | 8 | 2) Research the other 'special' signal traps. Use `man bash` for this. 9 | 10 | 3) Write a shell script that you can’t escape from (the machine it runs on must not be overloaded as a result!) in the terminal. 11 | 12 | 4) Try and escape from the shell script you created in 1) 13 | 14 | 5) Ask everyone you know if they can escape the shell script 15 | 16 | 6) If no-one can escape it, send it to the author :) 17 | -------------------------------------------------------------------------------- /slides/.gitignore: -------------------------------------------------------------------------------- 1 | *lock* 2 | -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirect_pipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/diagrams/pipes_and_redirect_pipe.png -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirect_pipe.xml: -------------------------------------------------------------------------------- 1 | 7Vpdc5s4FP01Hj+1AxL44zFx3e7OpLOZSWbbPqogg1qMGCHH9v76vYAECGE3yRp769Z5CLqSgnTPOdK9Nx7hxXr3QZAs/shDmoyQE+5G+N0IIRf7DvwqLPvKMvW9yhAJFqpBjeGB/UOVUc2LNiykuTFQcp5IlpnGgKcpDaRhI0LwrTlsxRPzrRmJqGV4CEhiWz+xUMaVdYamjf0PyqJYv9mdzKueNdGD1U7ymIR82zLh5QgvBOeyelrvFjQpnKf9Us17f6C3XpigqXzOhHy9+PAxery/S/7m37CHl/s7/garP5PLvd4xDcEBqsmFjHnEU5IsG+ttY73jPINhLhi/USn3Cj+ykRxMsVwnqpfumPzcev4Cz85b5Kvmu4Ivjm7sdSOVYv+53aim+brZTCtbel4uiZA3BfhgCBKS5yzQ5vcs0WsKNuKJhqpRuaHY+0H3alfxjQjoMZ9ixVMiIiqPDPRwTQPQD+VrCruAiYImRLIncyVEETmqx9VT7zmDNSJHiQ7PFOP2WkpahPpvVCtT0xrKwENrHY2pJNJLSIVeQSrBN2lYwuGckmLT11Fs+iOOXYQvs0H44k86fEEYgcbOyhjlgieSbNQ2RmiSgDNuv8JDVDzcCx7QPNd2eE/dZdHNJNM2ZpI+ZKREYQv3lEmcFRwJC55wUc7FIaGzVXVeCP6dtnomwYx+XdXve6JC0t1x+G2w9ASv43RX3xPb5pqpb8G4fcV01dxG2ADmxSh4l9StY+j2uGxPqL5KVM9QHx7mtLbU58472qu2MJz2Jpb2GvEJLbEHSdKQiNDuWcJFKyzigDKkibMpp5SntKM9ZSIJi9Li4gYoKdhvC50xiMhuVMeahWFJvT5dm3Q8gVB9t3s6+rZQvR6dosFkOrUAc18C2F8bmW3k9SLmOdbROr8wYjMLMecYYs6f6VUjZGnKnVxYU/plPQFI7GqAbkmRSCDnnmW0FYY0A7Qx04abwi3laGc8mi7GpZfyHBJauHDiwpxVUc04L6GuCcCVRiHZLbabchgtDg9nBV/ettaUHQyNXk+iHFjB0ggMftN6LG/5N+hQnMWBKKukzMRi4BhNh+CT28l45j1HtDvvi6WGI5RrEeqTAPeMcMGJcQ6vqWEeX6/SkRXmevZZ7PaFucMhg56DDC2imqsGBnfCGoQuDczccvf/sRbVqg18aXceKBQMVWXyzlY1+E+g+q8pMBo+O38pSHHhuQgb5UZ9V7ZqjadOWH8O4L1frqTj+Z0oZNYThfRFtbPBTlQ773ikYs1AS+Vt181AjMvP7m7CUB2eHh5x4P5sUHRfjOLKL356USw/xQyeypa9+pwG3amdVfbAi/qCzMEKdr4dyvzSeSWee6YC5xdOK337EPxdqmmLyv3xkXlewDwLsN/F0AavCe7+a/HShRvftwBrZ3PdK+uKMzpvYmZ0fYefi8+KjV2o7sfm2pPtLjSug4fDBprNl0qqfxI1X83By38B -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirect_redirect: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirect_redirect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/diagrams/pipes_and_redirect_redirect.png -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirect_redirect.xml: -------------------------------------------------------------------------------- 1 | 3VjbctowEP0ahvYhHVvGNjwGQtJ2mmmnZJq0b4olbLXG8sgyl359V7Z8w4YkDJQZ4AHpSIukPXtWa/esyWJ9J3Ac3HNCwx4yyLpn3fQQMi3bgB+FbHLEtQc54AtG9KQKmLG/VIPazk8ZoUljouQ8lCxugh6PIurJBoaF4KvmtDkPm6vG2KctYObhsI0+MiKDHB0it8I/UuYHxcqmM8pHFriYrE+SBJjwVQ2ypj1rIjiXeWuxntBQOa/wS253u2O03JigkXyNAZ/HQfR3tUzRffrr8535Ixh7V/pfErkpDkwJnF93uZAB93mEw2mFjiv0C+cxTDMB/E2l3Gj6cCo5QIFchHqUrpl8qrV/Qtv4gGzdvVHhYhSdTdGJpNg81Tu5mV10K7OsV9h5qVhSolfLz6cOtdNthQ94Kjy6x1eWDj8sfCr3zBuV5IIqKF9Q2BzYCRpiyZbNfWAdnn45r2IQGprENxCKDiBU8DQimceMY9LrHkav+xK/Z6DUPielepNLHKZ6pR5yQtju+Bkavmp8E9yjSVLgsEw51IqHJturgEk6i3HmphXk8CazcxaGEx5ykdlaBNPh3AM8kYL/obURxxvS53m53pIKSdf7+Wn7UxugoU6Z+s5wDDvvr6oMXF4QQT37FrfN0TkYnFNWRkNW+1V1RHHYrxSHdU5x2C1xPFCxYODmnnVd6kQUaugnsK5yEJQGor9HG+abtTG31bdTG9lHWfBI1vD8cxzNOMbLmjHRf9WM06IGtQmZSRwRLEh7ZKooajEEHpJNGprujnhEt7jREA6ZH0HXA49SwMfK3wwqvWs9sGCEZMLt4r0p5iMQZqNhgzDXahM26OALnYout0WX+Ra6vqYyTuUF87UlMBudma9hiy9jH1/Gp+iy+dnSk+MY5+Vn1OLnlil37LmVeKahfdfSASWbTYdk0HUtDdGzlV1LJ7h+utTRef04p3J/UR92lM2BWTh+jBPmwaTvlDChXh9UBXQ1qQDj0iovb3ITFvmqSAoUf3FehveTjL5SeVynRgMKPDipeg2hIkE5B3yTI0C6gxeKyjwiAHqnrj+jb26NvO+hSbli1yosqW2Pkva65RnjnQ8Jh6eFBAJT+cS6saveQ1bxXqFd4csh+OZh9nYkgKxBo1NkCHPrsWLgdsToqKtEOlmOMM1WkD4KcE+WJYyX0sLF5G7L3crdVjt3m10PfKcjBr2GmB1PERfLi2uejhfoVu9Cs7HaG2Vr+g8= -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirects_basic: -------------------------------------------------------------------------------- 1 | 7ZhbT9swFMc/TcUTU2KTXh5pYWPSblKRgKfJJKeJmRtHjtMLn34niXMxaTtA7TpVKw/k/H2Jc37526ft0cl89UmxJPoqAxA94gSrHr3qEeJSz8F/ubIulYF3UQqh4oHp1AhT/gxGNOPCjAeQWh21lELzxBZ9Gcfga0tjSsml3W0mhX3XhIXQEaY+E131jgc6KtUhGTT6DfAwqu7s9kdly5xVnc2TpBEL5LIl0esenSgpdXk1X01A5Mmr8lKO+7iltV6Ygli/ZsBN+DzJGPOffo5GanzvPSz86NzMkup19cAQ4PObUCodyVDGTFw36ljJLA4gn9XBqOnzRcoERRfFJ9B6bWCyTEuUIj0XphVWXN+3rh/yqT4Qz4RXKzN1EayrINZqfd8O2sPyuBlXRNXA8gnzx9qauCoLMlM+7MgWNS8gUyHoHf28Gi/6AuQccD04ToFgmi/sdTDzgoZ1v4YhXhiMb0BK/h2kg/chHfxHunGRCyYyc6ce6Qtc7vgRL8L84oeSPqRppeNt6qbO+2DTXkZcwzRhRZqWuIvbZGdciIkUUhVjacBgOPNRT7WSv6DV0veH8Dir77cApWG1m083n2YAGZpN05waxDNTLJs9uD4iovb+W503e2dwcUxbOZatdrtqj+bwXmkOekxzeB1z3IKac0xzj17WPlGNG14qZymuBGeQmU4y3W03zYDVhDrbYSb3zWaaefnfRjMVn3yEjHVLLz/7MVnf+bPJXPJXTdbvsCRdHlPN4oCpoNtynSPqEMIMaRuDne5YxvCCjZGY4GGMoY8ZBdTHeb45FoeXpmHOg6Bw+ibutvv3AMyjNjBa7ZItYBcbeJFD4Rp0cLlvwfXdGO5keVXfEmqDHZnXsMPL2cXL+RyfOJ+h7SdCjstntLXOi9wKzxXMWCZ0q9Br2ioxqYRvsjiGA67wu3G7OEy2Fofvp5siLh6HKHhNdFtUOudkW6UpkeBMFN+LI4QP8SFAkxcnnTvoGtEdbTrpDoa6Kl5brO8UpqeoWRy7JOkWHSdjQTqwLUg2HGnupkL/cGDc14DZUgyeDpchtbdGjx6MC4bNr2BFW+u3RHr9Gw== -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirects_basic.: -------------------------------------------------------------------------------- 1 | 7ZhbT9swFMc/TcUTU2KTXh5pYWPSblKRgKfJJKeJmRtHjtMLn34niXMxaTtA7TpVKw/k/H2Jc37526ft0cl89UmxJPoqAxA94gSrHr3qEeJSz8F/ubIulYF3UQqh4oHp1AhT/gxGNOPCjAeQWh21lELzxBZ9Gcfga0tjSsml3W0mhX3XhIXQEaY+E131jgc6KtUhGTT6DfAwqu7s9kdly5xVnc2TpBEL5LIl0esenSgpdXk1X01A5Mmr8lKO+7iltV6Ygli/ZsBN+DzJGPOffo5GanzvPSz86NzMkup19cAQ4PObUCodyVDGTFw36ljJLA4gn9XBqOnzRcoERRfFJ9B6bWCyTEuUIj0XphVWXN+3rh/yqT4Qz4RXKzN1EayrINZqfd8O2sPyuBlXRNXA8gnzx9qauCoLMlM+7MgWNS8gUyHoHf28Gi/6AuQccD04ToFgmi/sdTDzgoZ1v4YhXhiMb0BK/h2kg/chHfxHunGRCyYyc6ce6Qtc7vgRL8L84oeSPqRppeNt6qbO+2DTXkZcwzRhRZqWuIvbZGdciIkUUhVjacBgOPNRT7WSv6DV0veH8Dir77cApWG1m083n2YAGZpN05waxDNTLJs9uD4iovb+W503e2dwcUxbOZatdrtqj+bwXmkOekxzeB1z3IKac0xzj17WPlGNG14qZymuBGeQmU4y3W03zYDVhDrbYSb3zWaaefnfRjMVn3yEjHVLLz/7MVnf+bPJXPJXTdbvsCRdHlPN4oCpoNtynSPqEMIMaRuDne5YxvCCjZGY4GGMoY8ZBdTHeb45FoeXpmHOg6Bw+ibutvv3AMyjNjBa7ZItYBcbeJFD4Rp0cLlvwfXdGO5keVXfEmqDHZnXsMPL2cXL+RyfOJ+h7SdCjstntLXOi9wKzxXMWCZ0q9Br2ioxqYRvsjiGA67wu3G7OEy2Fofvp5siLh6HKHhNdFtUOudkW6UpkeBMFN+LI4QP8SFAkxcnnTvoGtEdbTrpDoa6Kl5brO8UpqeoWRy7JOkWHSdjQTqwLUg2HGnupkL/cGDc14DZUgyeDpchtbdGjx6MC4bNr2BFW+u3RHr9Gw== -------------------------------------------------------------------------------- /slides/diagrams/pipes_and_redirects_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/diagrams/pipes_and_redirects_basic.png -------------------------------------------------------------------------------- /slides/images/README: -------------------------------------------------------------------------------- 1 | Images in here 2 | -------------------------------------------------------------------------------- /slides/images/bash_startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/images/bash_startup.png -------------------------------------------------------------------------------- /slides/images/lbthw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/images/lbthw.png -------------------------------------------------------------------------------- /slides/images/me.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/images/me.jpg -------------------------------------------------------------------------------- /slides/images/shell_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/images/shell_history.png -------------------------------------------------------------------------------- /slides/introduction_to_bash.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/introduction_to_bash.pdf -------------------------------------------------------------------------------- /slides/introduction_to_bash.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmiell/introduction-to-bash/a0bac77c8d991ab4ea4f16c4d13db070a53c618c/slides/introduction_to_bash.pptx --------------------------------------------------------------------------------