linux:bash

Bash

Most of this comes from the greatest guide to bash : Bash Academy

Bash is a shell program written to listen for commands from users and execute them. There are other shell programs (C shell, Z shell, Korn shell, etc.) so it is important to know for what shell you are writing code for.

Bash uses text-based interface to interact with the users. It takes input (Std In) as text and displays output (Std Out) and error (Std Err) as text using a terminal emulator program.

Bash can be used interactively in the terminal emulator or non-interactively as a written script of commands that is executed.

When bash starts a program, the system creates a running process for it. Bash creates file descriptors and connects them to the same streams as it's own : (keyboard to FD:0 (input) and FD:1 (output) and FD:2 (error) to display 0). The pipe | is vastly used syntax sugar to connect the output of a command to the input of another.

Bash has synchronous command execution meaning it will execute commands one at a time and users cannot interact with bash while it is executing a command.

Bash is not a strict interpreter.

Beware of the ambiguity we are used to while expressing ourselves with human languages.

The discipline of writing clear, safe and precise code lies heavily upon the user.

Don't write bad bash code.

Bash commands tell bash to perform a certain unit of work. These units of work cannot be subdivided: bash needs to know the whole command to be able to execute it. Also, bash reads commands line-by-line.

There are different kinds of commands for different types of operations. Some commands group other commands into blocks or test their result.

Many command types are syntax sugar: their effect can be achieved differently, but they exist to make the job easier.

Before a command's name you can insert one or many var=value assignments. These variables will apply only to that one command.

Every command has a name that tells bash what to do.

Bash performs a search using this name and looks for :

  • alias (before anything else)
  • functions
  • built-in commands
  • programs installed (searches through PATH, also called external command)

Bash outputs an error if the command was not found.

echo

Displays target on standard output.

Example

$ name=john
$ echo name
name
$ echo $name
john

type

Displays information on a command, and where it is stored.

Note that the type command and the which program are different and give different outputs

Example

$ type ls
ls is aliased to 'ls -al'

Compound Commands compound a certain number of basic commands into a larger logical block.

If blocks

Execute commands according to conditional results

$ read -p "Name ? " name
Name ? Some Name
$ if [[ $name = $USER ]]; then
> echo "Hello, $USER."
> else
> echo "Hello, $name."
> fi
Hello, Some Name.

|| OR operator

This control operator tells bash to run a second command only if the first command before it failed. If the first command doesn't fail, the second command is entirely skipped.

This is useful for showing error messages when commands fail.

Example

$ rm hello.txt || echo "Could not find file."
rm: cannot remove 'hello.txt': No such file or directory
Could not find file.

Arguments are a sequence of characters considered as a single unit by the shell.

They can be a filename, a variable name, the name of a program or just a litteral.

Multiple arguments can be used, separated by a blank space

Quoting is the act of wrapping the argument using ' ' and “ ”.

Escaping is the act of adding a single \ before the character we want to escape.

Quoting is a better practice than escaping and should be use more often than not. Quoting is sometimes unnecessary but it is rarely wrong to quote.

If there is whitespace or a symbol in your argument, you must quote it.

  • Use “double quotes” for arguments that contain expansions (such as \$variable or $(command) expansions)
  • Use 'single quotes' for any other argument

'Single quotes' forces the entire quote to remain literal. “Double quotes” still allow some bash syntax.

Quotes are sometimes not necessary, but you can still quote to be safe.

Example of a major bash error due to lack of quoting

Expansion is the practice of replacing a part of our command code with a situationally specific piece of code.

In other words, we use expansion to build generic commands that adapt to different situations by expanding a part of our command, hereby replacing it with what the specific situation requires.

Expansion is always performed by bash itself, and always before actually running the command!

$ rm -v *
#The rm command will not see the * expansion.
#It will be given a list of all files in the current directory

Bash parameters

Bash parameters are regions in memory where you can temporarily store some information for later use.

Shell variables are a way to store a value to be read and/or modified later. Assignment is simple :

$ name=mh
$ echo "Hello, $name"
Hello, mh

There can be no whitespace around the = operator !

If a whitespace is required in the variable, use 'single quotes' to create a literal

A command expansion may be assigned to a variable :

$ contents="cat hello.txt" 
# Don't forget to "double quote" !

To re-use a variable we use parameter expansion, prefixing the name of our variable with a \$ sign. Whenever \$ is prefixed, something is probably being expanded (a parameter, the output of a command, or the result of an arithmetic operation)

RULE : Whenever you expand : you must “double quote” !

You can use {curly brackets} to tell bash where the variable starts and ends. This can be useful to prefix/suffix variables when they are output, such as :

$ name=mh time=8
$ echo "$name, it is ${time}h;"
mh, it is 8h.

We can use parameter expansion operators to modify the value that is expanded. This does not modify the value stored in the variable. See the guide for more details

WHITESPACE

Whitespace in bash have a special meaning : they split commands into arguments.

*

A star or asterix matches any kind of text, even no text at all.


?

A question mark matches any one single character.


[characters]

A set of characters within rectangular braces matches a single character, only if it's in the given set.


[[:classname:]]

When there is a set of colons directly inside the rectangular braces, you can specify the name of a class of characters instead of having to enumerate each character yourself.

Bash knows about various kinds of character classes. For example, if you use the [ [ :alnum: ] ] pattern, bash will match it against a character only if it is alphanumeric. Supported character classes include: alnum, alpha, ascii, blank, cntrl, digit, graph, lower, print, punct, space, upper, word, xdigit


Globs don't jump into subdirectories. Use globs in path names to expand to multiple directories

We can access extended globs in a current shell using

$ shopt -s extglob

See here for more explanations on using them

Tilde expansion happens earlier in the parser phrase, and is different from pathname expansion.

It is always replaced by an explicit pathname pointing to the home directory of the user or of another user explicitly named after the tilde.

$ echo 'I live in : ' ~
I live in : /home/mh
 
$ echo ~root
/var/root

Command expansions are a very useful value expansion example. You can call a command within a command by using $(…) syntax.

$ echo 'Hello world.' > hello.txt
$ cat hello.txt
Hello world.
$ echo "The file <hello.txt> contains: $(cat hello.txt)"
The file <hello.txt> contains: Hello world.

You must always use “double-quotes” when using value expansions or the $ sign will be output as a literal between 'single quotes'

By default, new commands inherit the shell's current file descriptors. We can use redirections to change where a command's input comes from and where its output should go to.

$ ls -l >myfiles.ls 2>/dev/null
 
#We redirect FD 1 to the file "myfiles.ls"
#and FD 2 to /dev/null
 
 
                 ╭──────────╮
    Keyboard ╾┬─╼┥0  bash  1┝╾─┬─╼ Display
              │  │         2┝╾─┘
              │  ╰─────┬────╯
              │        ╎
              │  ╭─────┴────╮
              └─╼┥0  ls    1┝╾───╼ myfiles.ls
                 │         2┝╾───╼ /dev/null
                 ╰──────────╯       

$ ls -l a b >myfiles.ls 2>&1
#Make FD 2 write to where FD 1 is writing
 
 
                 ╭──────────╮
    Keyboard ╾┬─╼┥0  bash  1┝╾─┬─╼ Display
              │  │         2┝╾─┘
              │  ╰─────┬────╯
              │        ╎
              │  ╭─────┴────╮
              └─╼┥0  ls    1┝╾─┬─╼ myfiles.ls
                 │         2┝╾─┘
                 ╰──────────╯

Duplicating file descriptors, otherwise referred to as “copying” file descriptors, is the act of copying one file descriptor's stream connection to another file descriptor.

As a result, both file descriptors are connected to the same stream.

Be careful not to write something like $ ls -l a b >myfiles.ls 2>myfiles.ls as due to the way streams are handled they will most likely end up mixing up and garbling the file 'myfiles.ls'

Order matters !

#These are not the same :
$ ls -l a b 2>&1 >myfiles.ls #Broken !
$ ls -l a b >myfiles.ls 2>&1 #Correct

For convenience :

#This convenience sugar syntax exists for directing both FD1 & FD2 to the same location
$ping 127.0.0.1 &>results

Use double brackets :

echo Hello >~/myfile
echo World >>~/myfile

Hashbangs

To make a script out of a file containing bash commands, add a hashbang at the beginning of the file :

This is a direct path to bash in many GNU/Linux distributions :

#!/bin/bash 

This rather more precise alternative invokes the 'env' program giving bash as an argument explicitly asking 'env' to find the path to bash and return it. It is safer and more inclusive for covering more exotic distributions and other operating systems :

#!/usr/bin/env bash

The script must be made executable before being run

$ chmod +x my_script.txt
$ ./my_script.txt

Note : the “.” means “current directory”

Command names with a slash are always considered direct pathnames to the program to execute.

  • linux/bash.txt
  • Last modified: 2022/02/28 14:54
  • by mh