[space]
between the option and its value : tail -n 5 someFile=
between the option and its value : tail --lines=5 someFile --
(i.e. [space]
+--
+[space]
) marks the end of options, everything after this is an argument (even though it looks like a valid option)-
The title of this article : "system variables" is intended as a generic term meaning both the environment variables and the shell variables
There's a subtle difference between them, which is mostly a matter of scope (source) :
\\!\\!
"' history)
#!/usr/bin/env bash tmpScript=$(mktemp tmpScript.XXXXXXXX) cat << 'EOF' > "$tmpScript" #!/usr/bin/env bash main() { echo $# : $@; } main "$@" EOF chmod +x "$tmpScript" ./"$tmpScript" ./"$tmpScript" foo ./"$tmpScript" foo bar ./"$tmpScript" 'foo bar' rm "$tmpScript"
0 : 1 : foo 2 : foo bar 1 : foo bar
$*
and $@
hold a
b
c
d
(4 distinct elements)"$*"
holds a b c d
(a single string having spaces). This is not iterable because of the explicit double-quotes."$@"
holds a
b
c d
(the 3 script parameters). The double quotes protect spaces when they are part of the parameter itself.#!/usr/bin/env bash # run : ./script.sh a b 'c d' echo -e '\nwith $* :' for i in $*; do echo $i; done echo -e '\nwith $@ :' for i in $@; do echo $i; done echo -e '\nwith "$*" :' for i in "$*"; do echo $i; done echo -e '\nwith "$@" :' for i in "$@"; do echo $i; done
with $* : a b c d with $@ : a b c d with "$*" : a b c d with "$@" : a b c d
"$@"
.hello this is the last argument of echo, even if there are some extra directives on the command line ./test this refers to "$testFile" coming after rm : the value has been substituted
${BASH_SOURCE[0]} |
$0 |
---|---|
|
|
#!/usr/bin/env bash echo -e " \$0:$0 \$BASH_SOURCE:$BASH_SOURCE \${BASH_SOURCE[0]}:${BASH_SOURCE[O]} " | column -s ':' -t
$0 ./test.sh $BASH_SOURCE ./test.sh ${BASH_SOURCE[0]} ./test.sh
$0 /bin/bash $BASH_SOURCE test.sh ${BASH_SOURCE[0]} test.sh
https_proxy
and ftp_proxy
.
~whatever
(source) :
~bob
— the tilde expression is expanded into Bob's home directory
~
is expanded into $HOME
[space][tab][newline]
"LANG
(1, 2, 3)LC_COLLATE
, LC_NUMERIC, LC_TIME
, ) is provided; it doesn’t override any setting, it provides the base value.LANGUAGE
LANGUAGE=en find charlie find: ‘charlie’: No such file or directory LANGUAGE=fr find charlie find: ‘charlie’: Aucun fichier ou dossier de ce type
:
)-separated list, to append a new value :
0
.
0 0 1 0 1 1 0
0 0 1 0 1 1 0
currentDir=$(pwd)
is double work since PWD
is already available.12321
$(whoami)
in scripts
This variable is often set as an environment variable by Bash login startup files, but it is not actually a Bash builtin (source).
TERM
environment variable is not / improperly set.So, if you just want to use an exclamation mark (not followed by a space) in a string, just simple-quote it :
ALT | CTRL | |
---|---|---|
a | move to the beginning of the line | |
c | send the SIGINT signal to stop the current process | |
d | delete everything after the cursor | exits the current shell |
e | move to the end of the line | |
l | clear the screen | |
q | resume from CTRL-s | |
r | search history of commands | |
s | pause output to the screen. Useful when running a verbose command :
for i in {1..1000}; do echo $i; sleep 0.1; done
|
|
x-x | Jump back and forth between the cursor position and the beginning of the line | |
z | send the SIGTSTP signal to the current foreground process to send it to the background. Use fg to bring it back to the foreground. | |
_ | Undo the last keystroke. Can be repeated to undo several keys back | |
. | paste the last argument of the previous command. Repeat to cycle back in previous commands |
I like bananas. bananas are great. bananas are what I prefer. My favorite fruit is bananas.
I like bananas. bananas are great. oranges are what I prefer. My favorite fruit is bananas.
echo -e "$testString" | awk '/prefer/ { gsub("bananas","oranges",$0); print $0 }'
oranges are what I prefer.
This only returns the processed line. Needs a little more work
echo -e "$testString" | sed -r '/prefer/ s/bananas/oranges/'
I like bananas. bananas are great. oranges are what I prefer. My favorite fruit is bananas.
testFile='./testFile'; echo -e "$testString" > "$testFile"; echo 'BEFORE :'; cat "$testFile"; sed -i '/prefer/ s/bananas/oranges/' "$testFile"; echo -e '\nAFTER :'; cat "$testFile"; rm "$testFile"
BEFORE : I like bananas. bananas are great. bananas are what I prefer. My favorite fruit is bananas. AFTER : I like bananas. bananas are great. oranges are what I prefer. My favorite fruit is bananas.
At first sight, sed looked simpler to me than Awk, but Awk is also able to work on a specific field of every line, or on a specific field of a specific line.
SPACE
characters (or any other character considered by the shell as a word separator, such as TAB
or NEWLINE
. Read more about IFS). Indeed, the SPACE
character is one of the common argument separators for the shell. Here come the quotes (but they must be set wisely ).file my
my file
file my
file1 file2 my
file1 file2 my
ls: cannot access my file*: No such file or directory
my file1 my file2
ls: cannot access my: No such file or directory ls: cannot access file*: No such file or directory
*
inside double quotes
ls: cannot access my file*: No such file or directory
*
outside double quotes
my file1 my file2
\
: preserve the literal value of the following character (except newline)''
: preserve the literal value of each character enclosed within the quotes. A single quote may not occur between single quotes, even when preceded by a \
.""
: preserve the literal value of all characters enclosed within the quotes, except for $
, ``
and \
.*
within quotes.
if
, for
, while
, [[
, case
, ... constructs. More){}
~
$myVariable
is substituted with its value$(command)
(or `command`
) is substituted with its output$((arithmeticExpression))
is substituted with its resultSPACE
(actually, each character of $IFS
is a delimiter).*
, ?
or[
, it is considered as a pattern and replaced with an alphabetically sorted list of file names matching the pattern (if any. Otherwise, leave the special character as-is)ls $fileName*
ls "$fileName*"
$fileName
is recognized as a variable and will be substituted[SPACE]
between my
and file
can not be used to split : ls "my file*"*
is taken literally, nothing to expand. No file named exactly my file* (i.e. having a *
in the file name) found, so no change : ls "my file*"ls "$fileName"*
$fileName
is recognized as a variable and will be substitutedOn UTF-8-capable systems (and generally speaking : extended charsets), characters lists such as a-z
include special characters like à
, é
or î
. Thus, characters lists (a-z
) can not be used in regular expressions to discriminate ASCII/non-ASCII characters. To do so, the solution is to build a complete list of all characters to match against with a regular expression : abcdefghijklmnopqrstuvwxyz
(source).
if
statement in Bash and in Ksh while the tested variable is actually unset :
Shell | Output | |
---|---|---|
if [ ${undefinedVariable} -eq 0 ]; then echo 'this is the "THEN" part'; else echo 'this is the "ELSE" part'; fi |
||
Bash | -bash: [: -eq: unary operator expected this is the "ELSE" part |
|
Ksh | ksh: [: argument expected this is the "ELSE" part |
|
if [ ${undefinedVariable} -ne 0 ]; then echo ''this is the "THEN" part'; else echo ''this is the "ELSE" part'; fi |
||
Bash | -bash: [: -ne: unary operator expected this is the "ELSE" part |
|
Ksh | ksh: [: argument expected this is the "ELSE" part |
We note that :
-eq
or a -ne
test is made, the else
part is executed.ssh bob@sshServer << EOC command1 command2 EOC
|
:cat << EOL | grep base Roses are #ff0000 Violets are #0000ff All my base are belong to you EOL
cat << EOF > newFile.txt some text, line 1 some text, line 2 some text, line n EOF
cat << EOF > newFile.txt line 1, not indented line 2, space-indented line 3, TAB-indented EOF cat newFile.txt; rm newFile.txt
$(command)
into the result of executing command)$((1+1))
into 2)cat << EOF current user : $USER today : $(date +"%a %b %d") 2 apples + 1 banana is $((2+1)) fruits EOF
current user : stuart today : Fri Nov 17 2 apples + 1 banana is 3 fruits
cat << 'EOF' simple quotes : $USER today : $(date +"%a %b %d") 2 apples + 1 banana is $((2+1)) fruits EOF
simple quotes : $USER today : $(date +"%a %b %d") 2 apples + 1 banana is $((2+1)) fruits
-
) -prefixed stop token (source) :This is a cosmetic hack improving readability of scripts since its allows indenting the heredocs too. The -
in the stop token suppresses leading tabs in the output.
spaces
, including the line of the stop token itself which must be TAB
-indented. If space
-indented, the stop token line becomes invisible which causes an error :
./myScript.sh: line 63: warning: here-document at line 23 delimited by end-of-file (wanted `EOF') ./myScript.sh: line 64: syntax error: unexpected end of file
<<
and -
TAB
characters are stripped out and both versions seem to produce the same outputcat << EOF indent : none indent : SPACE * 2 indent : TAB * 1 EOF cat <<-EOF indent : none indent : SPACE * 2 indent : TAB * 1 EOF
indent : none indent : SPACE * 2 indent : TAB * 1 indent : none indent : SPACE * 2 indent : TAB * 1
value=42; exampleFile='/tmp/myExampleFile.txt'; cat << EOF > "$exampleFile"
if (\$something > $value)
blah
if (\$anything < ($value/2))
pooh
else
nomnom
if (true || false || whatever)
who_cares
EOF
cat "$exampleFile"; rm "$exampleFile"
outputs :
if ($something > 42) blah if ($anything < (42/2)) pooh else nomnom if (true || false || whatever) who_cares
cat << EOSQL | sqlplus -s / as sysdba | grep -Ev '^$' SELECT DISTINCT(TRUNC(last_refresh)) FROM dba_snapshot_refresh_times; query1; query2; EOSQLOr even :
echo -e "query1;\nquery2;" | sqlplus -s / as sysdba | grep -Ev '^$'But it is simpler to do :
sqlplus -s / as sysdba << EOSQL | grep -Ev '^$' query1; query2; EOSQLRemember :
HEADER data line 1 data line 2 data line 3You can retrieve all lines except the header ...
echo -e "HEADER\ndata line 1\ndata line 2\ndata line 3" | grep -v 'HEADER'
echo -e "HEADER\ndata line 1\ndata line 2\ndata line 3" | sed -n '2,$ p'
echo -e "HEADER\ndata line 1\ndata line 2\ndata line 3" | sed 1d
echo -e "HEADER\ndata line 1\ndata line 2\ndata line 3" | tail -n +2
for i in {1..1000}; do echo $RANDOM >> data.txt; done
sed can do it :
sed -r 's/(^.{3}).*$/\1/g' data.txtBut it's overkill as cut can do it way easier :
cut -c -3 data.txt
user@host $ emacs & vlc & [1] 10367 [2] 10368Here, emacs is the 1st command we've launched, 1 is its jobId and 10367 is its PID.
[1]- Running emacs & [2]+ Running vlc &
[1]- 10367 Running emacs & [2]+ 10368 Running vlc &
Field Value | Description | Example |
---|---|---|
[n] | Job ID
|
|
+ or - |
|
|
10367 | PID | |
Job status :
|
The %jobId syntax used to refer to a job is also known as jobspec.
Syntax | Description | Example |
---|---|---|
{value1,value2,value_n} |
String generation Generate as many strings as the number of parameters, including a prefix and/or a suffix (examples) |
echo foo{1,2,3}bar foo1bar foo2bar foo3bar |
|
Generate a string for each parameter from the specified interval, including a prefix and/or a suffix
This construct should be preferred to seq because it won't start subprocesses.
|
echo test_{1..2}{a..b}_
test_1a_ test_1b_ test_2a_ test_2b_ echo {a..z..7}
a h o v |
${parameter:-default} |
Use default value If parameter is unset or null, default (which may be an expansion) is substituted. Otherwise, the value of parameter is substituted.
This can be used to make some function parameters optional (by giving them a default value when omitted) :
myFunction() { local myVariable=$1 local myOtherVariable=${2:-42} } |
key='value'; echo ${key:-'nothing'}; unset key; echo ${key:-'nothing'}
value nothing key='value'; default=42; echo ${key:-$default}; unset key; echo ${key:-$default}
value 42 |
${parameter:=default} |
Assign default value If parameter is unset or null, default (which may be an expansion) is assigned to parameter. The value of parameter is then substituted.
|
key='value'; result=${key:='nothing'}; echo $key; unset key; result=${key:='nothing'}; echo $key
value nothing |
${parameter:+value} |
Use value if parameter exists
If parameter exists, substitute (e.g. return) value (which may be an expansion).Otherwise (parameter is null or unset), return nothing. |
key='value'; echo ${key:+'key exists'}; unset key; echo ${key:+'key exists'}
key exists (empty line) |
${parameter?message} |
Return parameter, or display message if unset |
value=42; echo ${value?this variable is unset.}; unset value; echo ${value?this variable is unset.}
42 bash: value: this variable is unset. |
${parameter:offset:length} |
Substring Expansion Expands to up to length characters of parameter starting at the character specified by offset (0-indexed). If :length is omitted, go all the way to the end. If offset is negative (use parentheses!), count backward from the end of parameter instead of forward from the beginning.
The last character of a string :
${parameter:(-1):1} |
myString='0123456789'; echo ${myString:2}; echo ${myString:4:2}; echo ${myString:(-4):2};
23456789 45 67 myString='azerty'; echo ${myString:(-1):1}
y |
${#myString} |
String length
length of the string myString
Some documents/websites may state that
${#myArray} represents the number of items in the array myArray. This is wrong / obsolete (maybe it worked —with warnings— in previous Bash versions).(details) Looks like this works fine with multi-byte characters
|
myString='foo bar'; echo ${#myString}
7 a=$(echo -e '\u2639\u263a'); echo -e "$a\t${#a}"
☹☺ 2 |
${parameter#pattern} |
Remove pattern from the beginning of parameter
The pattern is matched against the beginning of parameter. The result is the expanded value of parameter with the shortest match deleted.
This can be used to retrieve the extension of a file name.
|
myString='abcdef'; echo ${myString#abc}; echo ${myString#[abc]}; myString='aaabbbccc'; echo ${myString#a*b}
def bcdef bbccc name=file.txt; echo ${name#*.}
txt |
${parameter##pattern} |
As above, but the longest match is deleted.
This can be used to retrieve the current directory name.
|
myString='aaabbbccc'; echo ${myString##a*b}
ccc log |
${parameter%pattern} |
Remove pattern from the end of parameter
The pattern is matched against the end of parameter. The result is the expanded value of parameter with the shortest match deleted.
This can be used to :
|
myString='aaabbbccc'; echo ${myString%a*b}; echo ${myString%b*c}
aaabbbccc aaabb |
${parameter%%pattern} |
As above, but the longest match is deleted. |
myString='aaabbbccc'; echo ${myString%%b*c}
aaa myString='abcdef-123'; echo ${myString%%-+([0-9])}
abcdef |
${parameter/search/replace} |
Results in the expanded value of parameter with the first match of search replaced by replace. |
myString='abcd abcd'; echo ${myString/cd/CD}
abCD abcd |
${parameter//search/replace} |
As above, but every match of search is replaced. |
myString='abcd abcd'; echo ${myString//cd/CD}
abCD abCD |
|
Return parameter with :
|
myString='hello, world'; echo ${myString^}; echo ${myString^^}; myString=${myString^^}; echo ${myString,}; echo ${myString,,}
Hello, world HELLO, WORLD hELLO, WORLD hello, world |
Code | Meaning |
---|---|
0 | success (aka " UNIX_SUCCESS " in my scripts) |
1 | catchall for general errors (aka " UNIX_FAILURE " in my scripts) |
2 | misuse of shell builtins |
124 | specific case with timeout |
126 | command invoked cannot execute |
127 | command not found |
128 | invalid argument to exit |
128 + n | fatal error signal n |
130 | script terminated by CTRL-c |
255* | exit status out of range |
Bits | Meaning |
---|---|
15-8 | shell command (child process) exit code |
7 | =1 if a core dump was produced |
6-0 | signal number that killed the process |
127
32512%255
127