Shell Language Syntax
Table of Contents
- Arrays
- Iterate array elements
- Join array elements
case
for
if
function
- Here document
- Here strings
- Shebang(
#!/usr/bin
) - Escaping quotes
- Usage of dash (
-
) in place of a filename, likecat -
Arrays reference
- Declare an array variable
- Usage
$ array=(one two three) $ echo "${array[@]}" one two three # NOTE: indexing starts at 0 in bash, # but some other shells like zsh starts at 1, # to be consistent with arugments indexing $ echo "${array[0]}" one $ echo "${array[2]}" three $ array[3]=four $ echo "${array[@]}" one two three four $ echo "${#array[@]}" # length 4 $ echo "${!array[@]}" # indexes 0 1 2 3 unset array[1] # delete an element echo "${array[@]}" one three four
- The difference between
${name[*]}
and${name[@]}
is corresponds to$*
vs$@
.
Iterate array elements howto
- Bash empty array expansion with
set -u
may cause an error. - There are many workarounds, but I think that it is the simplest to check empty before expansion
Join array elements howto
foo,bar,baz
case
reference
for
reference
- Put
; do
and; then
on the same line as thewhile
,for
orif
. - Use a
for
loop if you are confident that the input will not contain spaces or special characters (usually, this means not user input).
for i in 1 2 3 4 5; do
echo "$i"
done
for file in ~/repos/* ; do
echo "$file"
done
# continue and break
for i in 1 2 3; do
if [[ "$i" == 1 ]]; then
continue
fi
if [[ "$i" == 3 ]]; then
break
fi
echo "$i"
done
for (( i=1; i<=5; i++)); do
echo "$i"
done
# requires bash v3.0+
for i in {1..5}; do
echo "$i"
done
# requires bash v4.0+
for i in {0..10..2}; do
echo "$i"
done
if
reference
[
andtest
are available in POSIX shells[[
works only in Bash, Zsh and the Korn shell, and is more powerful[[
is preferred over[
,test
(from Google Shell Style Guide)
[ -a FILE ] |
True if FILE exists. |
[ -b FILE ] |
True if FILE exists and is a block-special file. |
[ -c FILE ] |
True if FILE exists and is a character-special file. |
[ -d FILE ] |
True if FILE exists and is a directory. |
[ -e FILE ] |
True if FILE exists. |
[ -f FILE ] |
True if FILE exists and is a regular file. |
[ -g FILE ] |
True if FILE exists and its SGID bit is set. |
[ -h FILE ] |
True if FILE exists and is a symbolic link. |
[ -k FILE ] |
True if FILE exists and its sticky bit is set. |
[ -p FILE ] |
True if FILE exists and is a named pipe (FIFO). |
[ -r FILE ] |
True if FILE exists and is readable. |
[ -s FILE ] |
True if FILE exists and has a size greater than zero. |
[ -t FD ] |
True if file descriptor FD is open and refers to a terminal. |
[ -u FILE ] |
True if FILE exists and its SUID (set user ID) bit is set. |
[ -w FILE ] |
True if FILE exists and is writable. |
[ -x FILE ] |
True if FILE exists and is executable. |
[ -O FILE ] |
True if FILE exists and is owned by the effective user ID. |
[ -G FILE ] |
True if FILE exists and is owned by the effective group ID. |
[ -L FILE ] |
True if FILE exists and is a symbolic link. |
[ -N FILE ] |
True if FILE exists and has been modified since it was last read. |
[ -S FILE ] |
True if FILE exists and is a socket. |
[ FILE1 -nt FILE2 ] |
True if FILE1 is newer than FILE2, or if FILE1 exists and FILE2 does not. |
[ FILE1 -ot FILE2 ] |
True if FILE1 is older than FILE2, or is FILE2 exists and FILE1 does not. |
[ FILE1 -ef FILE2 ] |
True if FILE1 and FILE2 refer to the same device and inode numbers. |
[ -o OPTIONNAME ] |
True if shell option "OPTIONNAME" is enabled. |
[ -z STRING ] |
True of the length if "STRING" is zero. |
[ -n STRING ] |
True if the length of "STRING" is non-zero. |
[ STRING ] |
True if the length of "STRING" is non-zero. |
[ STRING1 == STRING2 ] |
True if the strings are equal. |
[ STRING1 != STRING2 ] |
True if the strings are not equal. |
[ STRING1 < STRING2 ] |
True if "STRING1" sorts before "STRING2" |
[ STRING1 > STRING2 ] |
True if "STRING1" sorts after "STRING2" |
[ ARG1 OP ARG2 ] |
"OP" is one of -eq , -ne , -lt , -le , -gt or -ge . |
[ ! EXPR ] |
True if EXPR is false. |
[ ( EXPR ) ] |
Returns the value of EXPR. To override the normal precedence of operators. |
[ EXPR1 -a EXPR2 ] |
True if both EXPR1 and EXPR2 are true. |
[ EXPR1 -o EXPR2 ] |
True if either EXPR1 or EXPR2 is true. |
function
reference
- The keyword
function
is optional, but must be used consistently throughout a project. - If you're writing a package, separate package names with
::
.
#######################################
# Cleanup files from the backup dir
# Globals:
# BACKUP_DIR
# ORACLE_SID
# Arguments:
# None
# Returns:
# None
#######################################
cleanup() {
...
}
# If N is omitted, the return status is that of the
# last command executed within the function or script.
return [n]
Note that if you have
set -e
set at the top of your script and yourreturn 1
or any other number besides0
, your entire script will exit.exit
abandons the current shell.
- By default a variable is global.
- When we create a local variable within a function, it is only visible within that function.
var_change() {
local var1='local 1'
echo Inside function: var1 is $var1 : var2 is $var2
var1='changed again'
var2='2 changed again'
}
var1='global 1'
var2='global 2'
# only var2 changed
foo() {
return 0 # return returns a value from a function.
}
bar() {
exit 1 # exit abandons the current shell.
}
foo
echo 'hi'
bar
echo 'bye' # NOT printed
- http://tldp.org/LDP/abs/html/functions.html
- http://tldp.org/LDP/abs/html/complexfunct.html
- http://ryanstutorials.net/bash-scripting-tutorial/bash-functions.php
- https://google.github.io/styleguide/shell.xml?showone=Function_Comments#Function_Comments
- http://stackoverflow.com/questions/18042279/how-to-exit-a-function-in-bash
Here document reference
ONE TWO THREE
FOUR FIVE SIX
(Same as above)
\$ Working dir "$PWD" `pwd`
- For redirections and pipelining:
- https://unix.stackexchange.com/questions/88490/how-do-you-use-output-redirection-in-combination-with-here-documents-and-cat
Here strings reference
- The key difference from here documents is that, in here documents, the delimiters are on separate lines;
- Here strings are particularly useful when the last command needs to run in the current process
$ echo 'one two three' | read a b c
$ echo $a $b $c
# yields nothing, because 'read' ran on subshell
$ read a b c <<< 'one two three'
$ echo $a $b $c
one two three
Shebang(#!/usr/bin
) reference
- Use
#!/usr/bin/env bash
for portability - *Different nixes put
bash
in different places, and using/usr/bin/env
is a workaround to run the first bash found on the PATH.
Escaping quotes discussion
Escaping double quotes
- Escape it with backslash
"\""
- Double quoted shell expansion is valid within double quotes
$ echo 'echo $#' > arg-count
$ chmod +x arg-count
$ ./arg-count
0
$ echo "$(./arg-count $(echo foo bar))"
2
$ echo "$(./arg-count "$(echo foo bar)")"
1
Escaping single quotes within a single quoted string
alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
# ^^^^^ ^^^^^ ^^^^^ ^^^^
# 12345 12345 12345 1234
'
End first quotation which uses single quotes."
Start second quotation, using double-quotes.'
Quoted character."
End second quotation, using double-quotes.'
Start third quotation, using single quotes.
Or, use ANSI C string:($''
). We can escape a single quote with \'
. But in this way, we loses bash's literal meaning. Other meta character like \n
, \t
will also get a special meaning.
Usage of dash (-
) in place of a filename, like cat -
discussion
Using -
as a filename to mean stdin
/ stdout
is a convention that a lot of programs use. It is not a special property of the filename. If you want to use a file named as -
, you should pass the argument like ./-
.