We learn about setting up BASH for its running and coding environment. Here, you'll also learn the basic working mechanism for BASH programming language. You can use various editor to code your BASH scripts.
To work with BASH, you need to understand its origin and how it works in *nix environment.
BASH, like any other SHELL programming languages, is relying on *nix operating system pipeline management tool. In another word, it connects an input command, output of the command into another command's input like the way we join water pipes. This conforms the UNIX or Linux filesystem and its concepts. Here's an example:
Let's list all the files and directory using ls:
$ lsbin code_development Desktop Documents Downloads Music Pictures pkg Public snap src Templates VideosNext, let's pass it to a filter, say we want to remove "Desktop" from the ls output, we use pipe "|" command:
$ ls | grep -v "Desktop"bincode_developmentDocumentsDownloadsMusicPicturespkgPublicsnapsrcTemplatesVideosNext, let's say we want to modify the output to be a human dialog, we pass the filtered output again to xargs with echo command:
$ ls | grep -v "Desktop" | xargs -I {} echo "Here is a file:" {}Here is a file: binHere is a file: code_developmentHere is a file: DocumentsHere is a file: DownloadsHere is a file: MusicHere is a file: PicturesHere is a file: pkgHere is a file: PublicHere is a file: snapHere is a file: srcHere is a file: TemplatesHere is a file: VideosNotice the output has changed by piping the first command (ls) into the second command (grep) then lastly xargs-echo command. That's pipeline and almost all UNIX and LINUX system is based on this concept. BASH leverages this concept to reach its maximum potentials.
Another way to imagine shell scripts (including BASH) is like a video camera capturing an action moment. You can view the action repeatedly in the video and it will perform the exact same action every single time.
The difference here is that BASH is a terminal command capturing tool. It captures the list of commands (action) you want and packed it into a script. Then, you execute this script to repeat the set of commands again and again, consistently. Here's an example for scripting a Go programming language installation from their official site:
#!/bin/bashgo_version="$1"arch="$(dpkg --print-architecture)"go_pack="go${go_version}.linux-${arch}.tar.gz"go_install_path="/usr/local/go"go_pack_url="https://dl.google.com/go/$go_pack" sudo rm -rf "$go_install_path" > /dev/nullwget "$go_pack_url"tar -xvf "$go_pack"sudo mv go "$go_install_path"rm "./$go_pack"mkdir -p "${HOME}/src"mkdir -p "${HOME}/pkg"mkdir -p "${HOME}/bin" This way, every-time when there is a new version of Go releases, I can save my time to run this script instead of typing each line of commands in it.
BASH, by itself, doesn't have any working feature modules. It relies heavily on the executable programs available on a Unix system to get the job done.
In another word, BASH potentials become very limited if the core executables are not available, like coreutils or busybox. These libraries are the one responsible for facilitating commands like echo, [[, eval, grep, etc. Hence, always keep in mind that every SHELL script depends heavily on the operating system's facilities.
That being said, on the contrary, it very easy to learn BASH since there is only a little handful of things and conventions for you to remember.
Thankfully, most UNIX system already packed the core packages into it. You can try using which command to identify a particular command's availability.
$ which echo/bin/echo$If you want to know which package is responsible for this executable, use your package manager to identify it. Here is an example checking echo command from debian package manager using dpkg:
$ dpkg -S /bin/echo coreutils: /bin/echo$ dpkg -s coreutils Package: coreutilsEssential: yesStatus: install ok installedPriority: requiredSection: utilsInstalled-Size: 15103Maintainer: Michael Stone <mstone@debian.org>Architecture: amd64Multi-Arch: foreignVersion: 8.26-3Replaces: mktemp, realpath, timeoutPre-Depends: libacl1 (>= 2.2.51-8), libattr1 (>= 1:2.4.46-8), libc6 (>= 2.17), libselinux1 (>= 2.1.13)Conflicts: timeoutDescription: GNU core utilities This package contains the basic file, shell and text manipulation utilities which are expected to exist on every operating system. . Specifically, this package includes: arch base64 basename cat chcon chgrp chmod chown chroot cksum comm cp csplit cut date dd df dir dircolors dirname du echo env expand expr factor false flock fmt fold groups head hostid id install join link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf ptx pwd readlink realpath rm rmdir runcon sha*sum seq shred sleep sort split stat stty sum sync tac tail tee test timeout touch tr true truncate tsort tty uname unexpand uniq unlink users vdir wc who whoami yesHomepage: http://gnu.org/software/coreutils$In case you need a particular command, you'll need to search it out and install it into the system. There are various ways to install an open-source software. Perhaps, you can start with package manager.
BASH is one of the shell languages. It runs on a environment settings or a profile. As part of the core UNIX system, shell settings and profiles conformed to the Filesystem Hierarchy Standards. That also means for:
$HOME or ~./etc/.There are 2 types of configuration files: the profile, and the rc. They both have their different "trigger points". Hence, you should know the "trigger-point" for your settings and put them correctly into the correct file. Keep in mind that for MacOS, due to their modification in the operating system architecture, every terminal is a new login instance. That said: you need to study your operating system to know which configuration files make sense. Here is a summary of their respective differences:
profile, .bash_profile)Profile are the setting files configured for every login attempts before prompting. This is only executed once after you login into a machine whether locally or remotely (e.g. SSH).
bash.rc, .bashrc)rc are the setting files configured for every new interactive terminal prompt. This means that you already login into a system and you initiate a new terminal instance, like calling a new /bin/bash terminal for execution.
.bash_logout)Opposite to profile script, this script run for every logout. This is very useful for automatic cleanup work right before you logout from a machine whether locally or remotely (e.g. SSH).
There are other configuration files with different trigger points like completion However, here, we cover these 5 main configuration files since it is commonly known and maintained by any level of users. These are the 5 common configuration files:
/etc/profile, /etc/profile.d/* — system-wide terminal initialization file./etc/bash.bashrc — system-wide per-interactive configuration file. $HOME/.bash_profile — user-specific terminal initialization file.$HOME/.bashrc — user-specific per-interactive configuration file.$HOME/.bash_logout — user-specific terminal logout file.You should know the "area of effect" for your settings before inserting them into the configuration file. System-wide means it affects every single terminal level regardless user; user-specific means it only affects that particular user. The operating system first reads the system-wide configurations file, and then reads the user-specific configuration file, performing any necessary overrides from the latter to the former.
Now that you know the location of the configuration files, you should also know the master copy of them, just in case you screwed up somewhere and urgently need to restore it.
All UNIX system provides a backup user-specific configuration copy in /etc/skel directory.
That being said, you do not edit the files inside there; you should only use the correct file to restore your user-specific configuration file accordingly.
For beginner and (most) expert usage, we try to maintain 1 setting script as much as possible: $HOME/.bashrc. This way, it easier to backup and grow the settings as soon as your Unix experience grows. There are other forms of settings files like $HOME/.zshrc for different terminal types. In those scenarios, we apply symbolic link to it. Example:
$ ln -s $HOME/.bashrc $HOME/.zshrcThis way, ZSH terminal uses the BASH terminal configurations without much modifications.
NOTE: Since $HOME/.bashrc is usable across different shell terminals, you must keep it POSIX compliant to maintain portability.
It is very rare cases we apply system-wide configurations in /etc/profile. However, if you really need to do so, we create an independent bash script and put it inside /etc/profile.d/. The main reason is that /etc/profile loops over the /etc/profile.d/ directory and execute each scripts inside it. The caveat however, is that your setting file must be stateless and independent since you can't sequentially guess which script runs first.
Now that you know where to place your configurations. It's time to review each of the necessary settings and apply it into your $HOME/.bashrc. Again, in case you screwed up, you can always restore from the master copy.
This is governed by the $PS1 environment variable. This is the one that is responsible for printing the "prompt". The default is:
\s-\v\$ - produces a bash-4.1$ prompt like:bash-4.1$ You can modify accordingly to the manual. Example: say we change it to \u@\h:\w$. This gives us:
holloway@localhost:/var/log$To change it, simply export PS1 environment variable:
bash-4.1$ export PS1="\u@\h:\w$"holloway@localhost:/var/log$If you like it and you want to make it available permanently, save the command (export PS1="\u@\h:\w$") into your $HOME/.bashrc.
Most UNIX operating system has PS1 pre-configured. Here is an example from the default Debian $HOME/.bashrc:
if [ "$color_prompt" = yes ]; then PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u\[\033[00m\]:\[\033[01;34m\]\W\[\033[00m\]\$ 'else PS1='${debian_chroot:+($debian_chroot)}\u:\W\$ 'fiunset color_prompt force_color_prompt# If this is an xterm set the title to user@host:dircase "$TERM" inxterm*|rxvt*) PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" ;;*) ;;esacDebian set PS1 based on the condition whether the terminal has color supports and then terminal type. This yields a short and colorful prompt like (at this moment, Google New Site doesn't support color text):
holloway:~$We don't overwrite the default settings. You can save your PS1 command after this initialization. It will override whatever the default settings did.
These are the environment variables for each tools to initialize before use. If you start from a freshly installed operating system, there isn't much to do (since you haven't install any tool yet). Here is an example for Go programming language:
# Goexport GOROOT="/usr/local/go"export GOPATH="${HOME}"export GOBIN="${GOPATH}/bin"PATH="${GOROOT}/bin:${GOBIN}:$PATH"if [[ "$(which godoc)" != "" && "$(lsof -ni:"6060" -sTCP:LISTEN -t)" == "" ]]; then godoc -http :6060 -play &fiThese instructions came from Go programming language official site and is to append into $HOME/.bashrc. You can also append any tool changes or instructions into the configuration files.
Aliases, as a name said, is a re-representation of a command. This means that for commands that you commonly used but always comes with a set of argument settings, you can alias it. Example:
alias ll='ls -alF'alias la='ls -A'alias l='ls -CF'All 3 aliases are ls command with different arguments. Aliases can also form a new command based on the old command like:
# Nvidiaalias nvidia-off="sudo tee /proc/acpi/bbswitch <<<OFF"alias nvidia-on="sudo tee /proc/acpi/bbswitch <<<ON"However, we usually refrain from doing it since it's a kind of abuse and can cause confusion if an actual executable is available. A general rule of thumb for aliases is to use them with discipline and restricted to command alteration, not creation.
PATH is a special environment variable for terminal to search for executables in the specified location. You should not use it for your bash operations.
PATH contains a list of default paths for search, they are:
/bin/usr/bin/sbin/usr/sbinYou can expand the search directories by appending new paths into it. Example, say I want to have my user-specific bin folder and a current directory bin folder:
export PATH="${PATH}:./bin" # current directory bin folderexport PATH="${PATH}:${HOME}/bin" # user-specific bin folderYou basically append path using the colon (:) separator onto the $PATH variable itself.
In UNIX, we use a Desktop Entry Launcher as an application launcher (or "shortcut" in Microsoft Windows). Just like PATH, XDG_DATA_DIRS is another special environment variable for terminal to search for application launcher and populate it into your start menu or showing in your GUI. You should not use it for your bash operations. XDG_DATA_DIRS contains a list of default paths for search, they are:
/usr/local/share/usr/share/usr/share/gdm/var/lib/menu-xdg/usr/local/share//usr/share//usr/local/share//usr/share//usr/share/gdm//var/lib/menu-xdg/You can expand the path similar to PATH, like for example, I have all my launchers stored in ${HOME}/launchers folder:
export XDG_DATA_DIRS="${XDG_DATA_DIRS}:${HOME}/launchers" # user-specific launchers folderYou basically append path using the colon (:) separator onto the $XDG_DATA_DIRS variable itself.
There are other settings as well but these would be suffice for the basic bare-minimum usage. You can proceed to the next topic.