X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=bash.md;h=bb6114b4b9dea4e6e016bc36d23434de3e2d2542;hb=8401296c46a2c21b0783a2e74e78dd74d12e81d5;hp=9aefd8c6ab41c6c923b43bcba73792e8d07d20c9;hpb=e4a783533cafebf9787beb87ab06980bfacf94fa;p=twpd%2Fmaster.git diff --git a/bash.md b/bash.md index 9aefd8c..bb6114b 100644 --- a/bash.md +++ b/bash.md @@ -3,7 +3,7 @@ title: Bash scripting category: CLI layout: 2017/sheet tags: [Featured] -updated: 2019-03-23 +updated: 2020-07-05 keywords: - Variables - Functions @@ -18,6 +18,14 @@ Getting started --------------- {: .-three-column} +### Introduction +{: .-intro} + +This is a quick reference to getting started with Bash scripting. + +- [Learn bash in y minutes](https://learnxinyminutes.com/docs/bash/) _(learnxinyminutes.com)_ +- [Bash Guide](http://mywiki.wooledge.org/BashGuide) _(mywiki.wooledge.org)_ + ### Example ```bash @@ -102,9 +110,11 @@ See: [Unofficial bash strict mode](http://redsymbol.net/articles/unofficial-bash echo {A,B}.js ``` -| `{A,B}` | Same as `A B` | +| Expression | Description | +| ---------- | ------------------- | +| `{A,B}` | Same as `A B` | | `{A,B}.js` | Same as `A.js B.js` | -| `{1..5}` | Same as `1 2 3 4 5` | +| `{1..5}` | Same as `1 2 3 4 5` | See: [Brace expansion](http://wiki.bash-hackers.org/syntax/expansion/brace) @@ -138,6 +148,7 @@ See: [Parameter expansion](http://wiki.bash-hackers.org/syntax/pe) STR="/path/to/foo.cpp" echo ${STR%.cpp} # /path/to/foo echo ${STR%.cpp}.o # /path/to/foo.o +echo ${STR%/*} # /path/to echo ${STR##*.} # cpp (extension) echo ${STR##*/} # foo.cpp (basepath) @@ -151,7 +162,7 @@ echo ${STR/foo/bar} # /path/to/bar.cpp ```bash STR="Hello world" echo ${STR:6:5} # "world" -echo ${STR:-5:5} # "world" +echo ${STR: -5:5} # "world" ``` ```bash @@ -162,19 +173,19 @@ DIR=${SRC%$BASE} #=> "/path/to/" (dirpath) ### Substitution -| Code | Description | -| --- | --- | -| `${FOO%suffix}` | Remove suffix | -| `${FOO#prefix}` | Remove prefix | -| --- | --- | -| `${FOO%%suffix}` | Remove long suffix | -| `${FOO##prefix}` | Remove long prefix | -| --- | --- | -| `${FOO/from/to}` | Replace first match | -| `${FOO//from/to}` | Replace all | -| --- | --- | -| `${FOO/%from/to}` | Replace suffix | -| `${FOO/#from/to}` | Replace prefix | +| Code | Description | +| ----------------- | ------------------- | +| `${FOO%suffix}` | Remove suffix | +| `${FOO#prefix}` | Remove prefix | +| --- | --- | +| `${FOO%%suffix}` | Remove long suffix | +| `${FOO##prefix}` | Remove long prefix | +| --- | --- | +| `${FOO/from/to}` | Replace first match | +| `${FOO//from/to}` | Replace all | +| --- | --- | +| `${FOO/%from/to}` | Replace suffix | +| `${FOO/#from/to}` | Replace prefix | ### Comments @@ -192,12 +203,16 @@ comment ### Substrings -| `${FOO:0:3}` | Substring _(position, length)_ | -| `${FOO:-3:3}` | Substring from the right | +| Expression | Description | +| --------------- | ------------------------------ | +| `${FOO:0:3}` | Substring _(position, length)_ | +| `${FOO:(-3):3}` | Substring from the right | ### Length -| `${#FOO}` | Length of `$FOO` | +| Expression | Description | +| ---------- | ---------------- | +| `${#FOO}` | Length of `$FOO` | ### Manipulation @@ -211,15 +226,16 @@ echo ${STR^} #=> "Hello world!" (uppercase 1st letter) echo ${STR^^} #=> "HELLO WORLD!" (all uppercase) ``` - ### Default values -| `${FOO:-val}` | `$FOO`, or `val` if not set | -| `${FOO:=val}` | Set `$FOO` to `val` if not set | -| `${FOO:+val}` | `val` if `$FOO` is set | -| `${FOO:?message}` | Show error message and exit if `$FOO` is not set | +| Expression | Description | +| ----------------- | -------------------------------------------------------- | +| `${FOO:-val}` | `$FOO`, or `val` if unset (or null) | +| `${FOO:=val}` | Set `$FOO` to `val` if unset (or null) | +| `${FOO:+val}` | `val` if `$FOO` is set (and not null) | +| `${FOO:?message}` | Show error message and exit if `$FOO` is unset (or null) | -The `:` is optional (eg, `${FOO=word}` works) +Omitting the `:` removes the (non)nullity checks, e.g. `${FOO-val}` expands to `val` if unset otherwise `$FOO`. Loops ----- @@ -260,7 +276,7 @@ done ### Reading lines ```bash -< file.txt | while read line; do +cat file.txt | while read line; do echo $line done ``` @@ -327,12 +343,16 @@ fi ### Arguments -| Expression | Description | -| --- | --- | -| `$#` | Number of arguments | -| `$*` | All arguments | -| `$@` | All arguments, starting from first | -| `$1` | First argument | +| Expression | Description | +| --- | --- | +| `$#` | Number of arguments | +| `$*` | All positional arguments (as a single word) | +| `$@` | All positional arguments (as separate strings) | +| `$1` | First argument | +| `$_` | Last argument of the previous command | + +**Note**: `$@` and `$*` must be quoted in order to perform as described. +Otherwise, they do exactly the same thing (arguments as separate strings). See [Special parameters](http://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables). @@ -362,13 +382,15 @@ Note that `[[` is actually a command/program that returns either `0` (true) or ` | --- | --- | | `(( NUM < NUM ))` | Numeric conditions | -| Condition | Description | -| --- | --- | -| `[[ -o noclobber ]]` | If OPTIONNAME is enabled | -| --- | --- | -| `[[ ! EXPR ]]` | Not | -| `[[ X ]] && [[ Y ]]` | And | -| `[[ X ]] || [[ Y ]]` | Or | +#### More conditions + +| Condition | Description | +| -------------------- | ------------------------ | +| `[[ -o noclobber ]]` | If OPTIONNAME is enabled | +| --- | --- | +| `[[ ! EXPR ]]` | Not | +| `[[ X && Y ]]` | And | +| `[[ X || Y ]]` | Or | ### File conditions @@ -390,29 +412,19 @@ Note that `[[` is actually a command/program that returns either `0` (true) or ` ### Example ```bash -if ping -c 1 google.com; then - echo "It appears you have a working internet connection" -fi -```` - -```bash -if grep -q 'foo' ~/.bash_history; then - echo "You appear to have typed 'foo' in the past" -fi -``` - -```bash # String if [[ -z "$string" ]]; then echo "String is empty" elif [[ -n "$string" ]]; then echo "String is not empty" +else + echo "This never happens" fi ``` ```bash # Combinations -if [[ X ]] && [[ Y ]]; then +if [[ X && Y ]]; then ... fi ``` @@ -424,7 +436,7 @@ if [[ "$A" == "$B" ]] ```bash # Regex -if [[ "A" =~ "." ]] +if [[ "A" =~ . ]] ``` ```bash @@ -458,11 +470,13 @@ Fruits[2]="Orange" ```bash echo ${Fruits[0]} # Element #0 +echo ${Fruits[-1]} # Last element echo ${Fruits[@]} # All elements, space-separated echo ${#Fruits[@]} # Number of elements echo ${#Fruits} # String length of the 1st element echo ${#Fruits[3]} # String length of the Nth element echo ${Fruits[@]:3:2} # Range (from position 3, length 2) +echo ${!Fruits[@]} # Keys of all elements, space-separated ``` ### Operations @@ -547,11 +561,11 @@ set -o nounset # Exposes unset variables ### Glob options ```bash -set -o nullglob # Non-matching globs are removed ('*.foo' => '') -set -o failglob # Non-matching globs throw errors -set -o nocaseglob # Case insensitive globs -set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh") -set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb') +shopt -s nullglob # Non-matching globs are removed ('*.foo' => '') +shopt -s failglob # Non-matching globs throw errors +shopt -s nocaseglob # Case insensitive globs +shopt -s dotglob # Wildcards match dotfiles ("*.sh" => ".foo.sh") +shopt -s globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb') ``` Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob @@ -562,34 +576,42 @@ History ### Commands -| `history` | Show history | +| Command | Description | +| --------------------- | ----------------------------------------- | +| `history` | Show history | | `shopt -s histverify` | Don't execute expanded result immediately | ### Expansions -| `!$` | Expand last parameter of most recent command | -| `!*` | Expand all parameters of most recent command | -| `!-n` | Expand `n`th most recent command | -| `!n` | Expand `n`th command in history | +| Expression | Description | +| ------------ | ---------------------------------------------------- | +| `!$` | Expand last parameter of most recent command | +| `!*` | Expand all parameters of most recent command | +| `!-n` | Expand `n`th most recent command | +| `!n` | Expand `n`th command in history | | `!` | Expand most recent invocation of command `` | ### Operations -| `!!` | Execute last command again | -| `!!:s///` | Replace first occurrence of `` to `` in most recent command | -| `!!:gs///` | Replace all occurrences of `` to `` in most recent command | -| `!$:t` | Expand only basename from last parameter of most recent command | -| `!$:h` | Expand only directory from last parameter of most recent command | +| Code | Description | +| -------------------- | --------------------------------------------------------------------- | +| `!!` | Execute last command again | +| `!!:s///` | Replace first occurrence of `` to `` in most recent command | +| `!!:gs///` | Replace all occurrences of `` to `` in most recent command | +| `!$:t` | Expand only basename from last parameter of most recent command | +| `!$:h` | Expand only directory from last parameter of most recent command | `!!` and `!$` can be replaced with any valid expansion. ### Slices -| `!!:n` | Expand only `n`th token from most recent command (command is `0`; first argument is `1`) | -| `!^` | Expand first argument from most recent command | -| `!$` | Expand last token from most recent command | -| `!!:n-m` | Expand range of tokens from most recent command | -| `!!:n-$` | Expand `n`th token to last from most recent command | +| Code | Description | +| -------- | ---------------------------------------------------------------------------------------- | +| `!!:n` | Expand only `n`th token from most recent command (command is `0`; first argument is `1`) | +| `!^` | Expand first argument from most recent command | +| `!$` | Expand last token from most recent command | +| `!!:n-m` | Expand range of tokens from most recent command | +| `!!:n-$` | Expand `n`th token to last from most recent command | `!!` can be replaced with any valid expansion i.e. `!cat`, `!-2`, `!42`, etc. @@ -604,7 +626,7 @@ $((a + 200)) # Add 200 to $a ``` ```bash -$((RANDOM%=200)) # Random number 0..200 +$(($RANDOM%200)) # Random number 0..199 ``` ### Subshells @@ -627,6 +649,7 @@ python hello.py &>/dev/null # stdout and stderr to (null) ```bash python hello.py < foo.txt # feed foo.txt to stdin for python +diff <(ls -r) <(ls) # Compare two stdout without files ``` ### Inspecting commands @@ -686,6 +709,28 @@ printf "This is how you print a float: %f" 2 #=> "This is how you print a float: 2.000000" ``` +### Transform strings + +| Command option | Description | +| ------------------ | --------------------------------------------------- | +| `-c` | Operations apply to characters not in the given set | +| `-d` | Delete characters | +| `-s` | Replaces repeated characters with single occurrence | +| `-t` | Truncates | +| `[:upper:]` | All upper case letters | +| `[:lower:]` | All lower case letters | +| `[:digit:]` | All digits | +| `[:space:]` | All whitespace | +| `[:alpha:]` | All letters | +| `[:alnum:]` | All letters and digits | + +#### Example + +```bash +echo "Welcome To Devhints" | tr [:lower:] [:upper:] +WELCOME TO DEVHINTS +``` + ### Directory of script ```bash @@ -732,9 +777,13 @@ read -n 1 ans # Just one character ### Special variables -| `$?` | Exit status of last task | -| `$!` | PID of last background task | -| `$$` | PID of shell | +| Expression | Description | +| ---------- | -------------------------------------- | +| `$?` | Exit status of last task | +| `$!` | PID of last background task | +| `$$` | PID of shell | +| `$0` | Filename of the shell script | +| `$_` | Last argument of the previous command | See [Special parameters](http://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables). @@ -748,6 +797,22 @@ cd - pwd # /home/user/foo ``` +### Check for command's result + +```bash +if ping -c 1 google.com; then + echo "It appears you have a working internet connection" +fi +``` + +### Grep check + +```bash +if grep -q 'foo' ~/.bash_history; then + echo "You appear to have typed 'foo' in the past" +fi +``` + ## Also see {: .-one-column}