Tuesday, August 16, 2016

The Bash "Arrays"

  • Bash provides only 1 dimensional array.
     
  • Bash provides both indexed array and associative arrays(also called as hashes in other languages)

  • Any variables can be used as an indexed array.

  • The declare builtin can be used to explicitly declare an array.

  • There is no size limitations on array.

  • Indexed Arrays dont need the members be indexed or assigned continously.

  • Indexed arrays can be referenced using integers and are zero based.

  • Associative Arrays use arbitrary strings.

  • Following are two ways to create indexed arrays :
    • Implicit way : Assign Value to any variable using below syntax
name[subscript]=value
The subscript is treated as an arithmetic expression that must evaluate to a number.

  • Explicit way: Use declare -a
declare -a name

Examples:
  1. Implicit method:
[sukul1@lldd010 ~]$ array2[1]=sukul
[sukul1@lldd010 ~]$ array2[0]=mahadik
[sukul1@lldd010 ~]$  declare -a
declare -a BASH_ARGC='()'
declare -a BASH_ARGV='()'
declare -a BASH_LINENO='()'
declare -a BASH_SOURCE='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="1" [2]="2" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -a DIRSTACK='()'
declare -a FUNCNAME='()'
declare -a GROUPS='()'
declare -a PIPESTATUS='([0]="0")'
declare -a array2='([0]="mahadik" [1]="sukul")'

  1. Explicit method:
[sukul1@lldd010 ~]$ declare -a array3
[sukul1@lldd010 ~]$ declare -a
declare -a BASH_ARGC='()'
declare -a BASH_ARGV='()'
declare -a BASH_LINENO='()'
declare -a BASH_SOURCE='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="1" [2]="2" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -a DIRSTACK='()'
declare -a FUNCNAME='()'
declare -a GROUPS='()'
declare -a PIPESTATUS='([0]="0")'
declare -a array3='()'
[sukul1@lldd010 ~]$

The declare -a shows all the arrays defined in the current environment.

  • To create associative arrays we can use declare builtin.
declare -A name

Example:
[sukul1@lldd010 ~]$ declare -A AssoArray
[sukul1@lldd010 ~]$ declare -A
declare -A AssoArray='()'
declare -A BASH_ALIASES='()'
declare -A BASH_CMDS='()'
[sukul1@lldd010 ~]$


  • Attributes may be specified for an array variable using the declare and readonly builtins. Each attribute applies to all members of an array.
Thus we can make arrays readonly.

[sukul1@lldd010 ~]$ declare -A AssoArray
[sukul1@lldd010 ~]$ declare -A
declare -A AssoArray='()'
declare -A BASH_ALIASES='()'
declare -A BASH_CMDS='()'
[sukul1@lldd010 ~]$ declare -Ar readArray
[sukul1@lldd010 ~]$ readonly AssoArray
[sukul1@lldd010 ~]$ declare -A
declare -Ar AssoArray='()'
declare -A BASH_ALIASES='()'
declare -A BASH_CMDS='()'
declare -Ar readArray='()'
[sukul1@lldd010 ~]$

  • Once Arrays are created arrays can be assigned values one element at a time as shown below:

     
[sukul1@lldd016 ~]$ declare -a arr1
[sukul1@lldd016 ~]$ arr1[0]=25
[sukul1@lldd016 ~]$ arr1[1]=34
[sukul1@lldd016 ~]$ declare -A ARR1
[sukul1@lldd016 ~]$ ARR1['name']=sukul
[sukul1@lldd016 ~]$ ARR1['surname']=mahadik

Other simpler way to do this is to use compound assignments of the form
 
name=(value1 value2 ... )
 
where each value is of the form [subscript]=string. Indexed array assignments do not require anything but string.
When assigning to indexed arrays, if the optional subscript is supplied, that index is assigned to starts at zero.
When assigning to an associative array, the subscript is required.

Note that for indexed assigning values like this would implicitly create the array itself. However note that does not work with Associative arrays.
Associative arrays need to be created first before we do such compound assignment.

[sukul1@lldd016 ~]$ declare -a arr2
[sukul1@lldd016 ~]$ arr2=(23 45 23 21 56)
[sukul1@lldd016 ~]$ declare -a
declare -a arr2='([0]="23" [1]="45" [2]="23" [3]="21" [4]="56")'

[sukul1@lldd016 ~]$ declare -A ARR2
[sukul1@lldd016 ~]$ ARR2=([name]=sukul [surname]=mahadik)
[sukul1@lldd016 ~]$ declare -A
declare -A ARR2='([surname]="mahadik" [name]="sukul" )'

In below example we can see that compound assignment can actually create indexed array, but that does not work with associative arrays.
Associative arrays need declaration.

[sukul1@lldd016 ~]$ arr3=(p1 p2 p3 p4)
[sukul1@lldd016 ~]$ declare -a
declare -a arr3='([0]="p1" [1]="p2" [2]="p3" [3]="p4")'

[sukul1@lldd016 ~]$ ARR3=([surname]=mahadik)
[sukul1@lldd016 ~]$ declare -A
declare -A ARR1='([surname]="mahadik" [name]="sukul" )'
declare -A ARR2='([surname]="mahadik" [name]="sukul" )'
declare -A BASH_ALIASES='()'
declare -A BASH_CMDS='()'
[sukul1@lldd016 ~]$ declare -a
declare -a ARR3='([0]="mahadik")'
We can see above that associative array was not created implicitly using compound assignment method.
So remember that associative arrays need declaration.

  • When assigning to or referencing an indexed array, if name is subscripted by a negative number, that number is interpreted as relative to
one greater than the maximum index of name, so negative indices count back from the end of the array, and an index of -1 references the last element.

However for some reason this does not seem to work on my bash

[sukul1@lldd016 ~]$ echo ${arr2[1]}
45
[sukul1@lldd016 ~]$ echo ${arr2[-1]}
-bash: arr2: bad array subscript

[sukul1@lldd016 ~]$

  • Any element of an array may be referenced using ${name[subscript]}.
The braces are required to avoid conflicts with the shell’s filename expansion operators.
Remember that the braces are required. Ex: 2nd element of array arr2 is arr2[1] . So to check the value in it we need to put ${} around it, just as we do with variables.
But here remember that braces are always required.

  • If the subscript is ‘@’ or ‘*’, the word expands to all members of the array name.
These subscripts differ only when the word appears within double quotes.
If the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS variable, and ${name[@]} expands each element of name to a separate word.

[sukul1@lldd016 ~]$ echo ${arr2[*]}
23 45 23 21 56
[sukul1@lldd016 ~]$ echo ${arr2[@]}
23 45 23 21 56

Following shows the difference between * and  when used in double quotes.
With * result is one single word.
[sukul1@lldd016 ~]$ for i in "${arr2[*]}"
> do
> echo $i
> done
23 45 23 21 56
[sukul1@lldd016 ~]$ for i in "${arr2[@]}"
> do
> echo $i
> done
23
45
23
21
56
[sukul1@lldd016 ~]$

This is analogous to the expansion of the special parameters ‘@’ and ‘*’.

  • ${#name[subscript]} expands to the length of ${name[subscript]}.
If subscript is ‘@’ or ‘*’, the expansion is the number of elements in the array.

Following shows how to find length of an element of array. Its no different that finding size of any other variable.

[sukul1@lldd016 ~]$ echo ${#ARR1[surname]}
7
[sukul1@lldd016 ~]$ echo ${ARR1[surname]}
mahadik
[sukul1@lldd016 ~]$

ARR1[surname] is nothing but a variable, just that this variable is part of any array.

Following shows how to find the size of array

[sukul1@lldd016 ~]$ echo ${#ARR1[*]}
2
[sukul1@lldd016 ~]$

  • Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0.

[sukul1@lldd016 ~]$ echo ${arr2[0]}
23
[sukul1@lldd016 ~]$ echo $arr2
23
[sukul1@lldd016 ~]$

  • It is possible to obtain the keys (indices) of an array as well as the values.
${!name[@]} and ${!name[*]} expand to the indices assigned in array variable name.
The treatment when in double quotes is similar to the expansion of the special parameters ‘@’ and ‘*’ within double quotes.


[sukul1@lldd016 ~]$ echo ${!arr2[*]}
0 1 2 3 4

  • The unset builtin is used to destroy arrays. unset name[subscript] destroys the array element at index subscript.
unset name, where name is an array, removes the entire array. A subscript of ‘*’ or ‘@’ also removes the entire array

  • The declare, local, and readonly builtins each accept a -a option to specify an indexed array and a -A option to specify an associative array.
If both options are supplied, -A takes precedence.

  • The read builtin accepts a -a option to assign a list of words read from the standard input to an array, and can read values from the standard input into individual array elements.

  • The set and declare builtins display array values in a way that allows them to be reused as input.

Monday, August 15, 2016

The Bash "Alias"

Aliases

  • Aliases allow a string to be substituted for a word when it is used as the first word of a simple command.

    The
    first word of each simple command, if unquoted, is checked to see if it has an alias.
    If so, that word is replaced by the text of the alias.

  • The characters ‘/’, ‘$’, ‘‘’, ‘=’ and any of the shell metacharacters or quoting characters listed above may not appear in an alias name.The replacement text may contain any valid shell input, including shell metacharacters.

  • You can nest aliases.This means that if the 1st word of a simple command in a alias, then its replaced with the replacement text. However if the 1st word of the replacement text is also another alias then that is also replaced 
 
Example:
/export/home/sukul1$alias eh='echo $HOME'
/export/home/sukul1$alias ep='eh $PWD'

/export$eh
/export/home/sukul1
export$ep
/export/home/sukul1 /export

When we run ep, the 1st word (.i.e ep) is checked to see if it matches any alias, and the replacement text "eh $PWD" is used. However 1st word of replacement string is also a alias and so that is also replaced with "echo $HOME". So the command becomes "echo $HOME $PWD"


  • However very imp to note that an alias does not replace itself, which avoids the possibility of infinite recursion in handling an alias such as the following:
$ alias ls='ls -F'
This means that if the 1st word of the replacement text is same as the alias itself being replaced, then its not replaced twice.

When we run ls, its replaced with "ls -F". Here the 1st word of the replacement string is same as the alias being expanded. So its not replaced twice.

So basically 1st word of alias replacement text can be some other alias, and shell will expand that too.

  • The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one may alias ls to "ls -F", for instance, and Bash does not try to recursively expand the replacement text.

  • Aliases are created and listed with the alias command, and removed with the unalias command.
    There is no mechanism for using arguments in the replacement text, as in csh. If arguments are needed, a shell function should be used

  • By default the option expand_aliases is enabled for interactive shells. Check $BASHOPTS.Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt.To use Aliases within scripts we need to set this shopt option.



The Bash "Functions"

Functions

  • Shell functions are a way to group commands for later execution using a single name for the group. They are executed just like a "regular" command.

  • When the name of a shell function is used as a simple command name, the list of commands associated with that function name is executed.

  • Shell functions are executed in the current shell context; no new process is created to interpret them. This is similar to  running using . Or source builtin.

  • Functions are declared using this syntax:

name () compound-command [ redirections ]
or
function name [()] compound-command [ redirections ]

This defines a shell function named name. The reserved word function is optional. Without the keyword function, the () indicate that this is a function.
If the function reserved word is supplied, the parentheses are optional.

  • The body of the function is the compound command compound-command.
    That command is usually a list enclosed between { and }, but may be any compound command like if,case etc. compound-command is executed whenever name is specified as the name of a command.

  • Any redirections associated with the shell function are performed when the function is executed.

  • A function definition may be deleted using the -f option to the unset builtin
     
  • With declare, functions can be made readonly also.
     
[sukul1@blph512 teradata]$ readonly showme
[sukul1@blph512 teradata]$ unset showme
-bash: unset: showme: cannot unset: readonly variable
[sukul1@blph512 teradata]$ declare +r showme
-bash: declare: showme: readonly variable
[sm017r@blph512 teradata]$

                  Readonly functions cannot be unset or made non-readonly

  • The exit status of a function definition is zero unless a syntax error occurs or a readonly function with the same name already exists.
     
  • When executed, the exit status of a function is the exit status of the last command executed in the body.

  • In the most common usage the curly braces that surround the body of the function must be separated from the body by blanks or newlines. This is because the braces are reserved words and are only recognized as such when they are separated from the command list by whitespace or another shell metacharacter.

  • When a function is executed, the arguments to the function become the positional parameters during its execution.

    The special parameter ‘#’ that expands to the number of positional parameters is updated to reflect the change. 

    Special parameter 0 is unchanged.(very imp)

[sukul1@blph512 teradata]$ function showme ()
> {
> echo $0 $LOGNAME
> }
[sukul1@blph512 teradata]$ showme
-bash sukul1

                  Note that value of $0 is not the function name, its the shell name.
                  If this is run inside a script, $0 will be script name.

  • The first element of the FUNCNAME variable is set to the name of the function while the function is executing.

  • The FUNCNEST variable, if set to a numeric value greater than 0, defines a maximum function nesting level. Function invocations that exceed the limit cause the entire command to abort.

  • If the builtin command return is executed in a function, the function completes and execution resumes with the next command after the function call.

  • When a function completes, the values of the positional parameters and the special parameter ‘#’ are restored to the values they had prior to the function’s execution.

  • If a numeric argument is given to return, that is the function’s return status; otherwise the function’s return status is the exit status of the last command executed before the return.

  • Variables local to the function may be declared with the local builtin. These variables are visible only to the function and the commands it invokes

  • Function names and definitions may be listed with the -f option to the declare (typeset) builtin command

     
[sukul1@blph512 teradata]$ declare -f showme
showme ()
{
    echo $0 $LOGNAME
}

  • The -F option to declare or typeset will list the function names only

[sukul1@blph512 teradata]$ declare -F
declare -f _module
declare -f _module_avail
declare -f _module_long_arg_list
declare -f _module_not_yet_loaded
declare -fx module
declare -f showme

  • Functions may be exported so that subshells automatically have them defined with the -f option to the export builtin

[sukul1@blph512 teradata]$ export -f showme
[sukul1@blph512 teradata]$ declare -F
declare -f _module
declare -f _module_avail
declare -f _module_long_arg_list
declare -f _module_not_yet_loaded
declare -fx module
declare -fx showme

            So functions can be declared, exported and unset just like variables.

  • With declare, functions can be made readonly also.
     
[sukul1@blph512 teradata]$ readonly showme
[sukul1@blph512 teradata]$ unset showme
-bash: unset: showme: cannot unset: readonly variable
[sukul1@blph512 teradata]$ declare +r showme
-bash: declare: showme: readonly variable
[sukul1@blph512 teradata]$

                  Readonly functions cannot be unset or made non-readonly


  • Functions may be recursive. The FUNCNEST variable may be used to limit the depth of the function call stack and restrict the number of function invocations. By default, no limit is placed on the number of recursive calls.

Invoking bash

  • Syntax:

bash [long-opt] [-ir] [-abefhkmnptuvxdBCDHP] [-o option] [-O shopt_option] [argument ...]
bash [long-opt] [-abefhkmnptuvxdBCDHP] [-o option] [-O shopt_option] -c string [argument ...]
bash [long-opt] -s [-abefhkmnptuvxdBCDHP] [-o option] [-O shopt_option] [argument ...]

  • All of the single-character options used with the set builtin can be used as options when the shell is invoked.
This refers to the list [-abefhkmnptuvxdBCDHP] in the above example.

  • In addition, there are several multi-character options that you can use. These options must appear on the command line before the single-character options to be recognized. This refers to the  [long-opt] which are explained below

--help
Display a usage message on standard output and exit successfully.

/export/home/sm017r$bash --help
GNU bash, version 3.2.57(1)-release-(sparc-sun-solaris2.10)
Usage:  bash [GNU long option] [option] ...
        bash [GNU long option] [option] script-file ...
GNU long options:
        --debug
        --debugger
        --dump-po-strings
        --dump-strings
        --help
        --init-file
        --login
        --noediting
        --noprofile
        --norc
        --posix
        --protected
        --rcfile
        --restricted
        --verbose
        --version
        --wordexp
Shell options:
        -irsD or -c command or -O shopt_option          (invocation only)
        -abefhkmnptuvxBCHP or -o option
Type `bash -c "help set"' for more information about shell options.
Type `bash -c help' for more information about shell builtin commands.
Use the `bashbug' command to report bugs.
/export/home/sm017r$

--init-file filename
--rcfile filename
Execute commands from filename (instead of ~/.bashrc) in an interactive shell.
--login
Equivalent to -l. This makes the shell behave as a login shell (which makes login start up scripts to be executed)

--noediting
Do not use the gnu Readline library to read command lines when the shell is interactive.
--noprofile
Don’t load the system-wide startup file /etc/profile or any of the personal initialization files ~/.bash_profile, ~/.bash_login, or ~/.profile when Bashis invoked as a login shell.

--norc
Don’t read the ~/.bashrc initialization file in an interactive shell. This is on by default if the shell is invoked as sh.
--posix
Change the behavior of Bash where the default operation differs from the posix standard to match the standard.
This is intended to make Bash behave as a strict superset of that standard.

--restricted
Make the shell a restricted shell
--verbose
Equivalent to -v. Print shell input lines as they’re read.
This is similar to use set -v

This prints every line that bash is executing.

[sukul1@cldi016 ~]$ bash --verbose script1.bash
module () {  eval `/usr/bin/modulecmd bash $*`
}
#! /usr/bin/bash
echo "che"
che
echo $PATH
/opt/teradata/client/15.10/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/sm017r/bin
[sm017r@cldi016 ~]$

[sukul1@cldi016 ~]$ bash script1.bash
che
/opt/teradata/client/15.10/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/sm017r/bin
[sm017r@cldi016 ~]$


--version
Show version information for this instance of Bash on the standard output and  exit successfully.

[sm017r@cldi016 ~]$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
[sm017r@cldi016 ~]$


  • There are several single-character options that may be supplied at invocation which are not available with the set builtin.

-c
Read and execute commands from the first non-option argument after processing the options, then exit.
Any remaining arguments are assigned to the positional parameters, starting with $0.

[sukul1@lldd010 ~]$ bash -c ls| wc -l
6
[sukul1@lldd010 ~]$

-i
Force the shell to run interactively.
-l
Make this shell act as if it had been directly invoked by login. When the shell is interactive, this is equivalent to starting a login shell with ‘exec -l bash’.
When the shell is not interactive, the login shell startup files will be executed.
‘exec bash -l’ or ‘exec bash --login’ will replace the current shell with a Bash login shell.
This would cause the /etc/profile and .bash_profile to be executed.
-r
Make the shell a restricted shell
-s
If this option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.

[sukul1@cldi016 ~]$ bash -s Sukul Mukul
[sukul1@cldi016 ~]$ echo $1
Sukul
[sukul1@cldi016 ~]$ echo $2
Mukul
[sukul1@cldi016 ~]$

We can see that we started a interactive shell by set positional parameters using -s option.
[-+]O [shopt_option]
shopt option is one of the shell options accepted by the shopt builtin

If shopt option is present, -O sets the value of that option; +O unsets it. If shopt option is not supplied, the names
and values of the shell options accepted by shopt are printed on the standard
output. If the invocation option is +O, the output is displayed in a format that may be reused as input.



--
A -- signals the end of options and disables further option processing.
Any arguments after the -- are treated as filenames and arguments.



  • A login shell is one whose first character of argument zero is ‘-’, or one invoked with the --login option.

[sukul1@cldi016 ~]$ echo $0
-bash
[sukul1@cldi016 ~]$

  • An interactive shell is one started without non-option arguments, unless -s is specified, without specifying the -c option, and whose input and output are both connected to terminals or one started with the -i option.

If arguments remain after option processing, and neither the -c nor the -s option has been supplied, the first argument is assumed to be the name of a file containing shell commands When Bash is invoked in this fashion, $0 is set to the name of the file, and the positional parameters are set to the remaining arguments. Bash reads and executes commands from this file, then exits. Bash’s exit status is the exit status of the last command executed in the script. If no commands are executed,the exit status is 0.