2015年12月14日 星期一

[Linux 文章收集] Shell - HowTo: Use Bash Parameter Substitution Like A Pro

Source From Here 
Preface 
The $ character is used for parameter expansion, and command substitution. You can use it for manipulating and/or expanding variables on demands without using external commands such as sed or awk

#1: Getting Up Default Shell Variables Value 
The syntax is as follows: 
  1. ${parameter:-defaultValue}  
  2. var=${parameter:-defaultValue}  
If parameter not set, use defaultValue. In this example, your shell script takes arguments supplied on the command line. You'd like to provide default value so that the most common value can be used without needing to type them every time. If variable $1 is not set or passed, use root as default value for u
Consider the following example: 
- t1.sh 
  1. #!/bin/bash  
  2. _jail_dir="${1:-/home/phpcgi}"  
  3. echo "Setting php-cgi at ${_jail_dir}..."  
  4. # rest of the script ...  
Execution example: 
# ./t1.sh /tail
Setting php-cgi at /tail...
# ./t1.sh /home/httpd/jail
Setting php-cgi at /home/httpd/jail...
# ./t1.sh // <--- as="" at="" default="" dir="" font="" home="" jail="" php="" phpcgi="" set="" value="">
Setting php-cgi at /home/phpcgi...

Here is another handy example: 
  1. _mkdir(){  
  2.         local d="$1"               # get dir name  
  3.         local p=${2:-0755}      # get permission, set default to 0755  
  4.         [ $# -eq 0 ] && { echo "$0: dirname"return; }  
  5.         [ ! -d "$d" ] && mkdir -m $p -p "$d"  
  6. }  
Use this substitution for creating failsafe functions and providing missing command line arguments in scripts. 

#1.1: Setting Default Values 
The syntax is as follows: 
  1. ${var:=value}  
  2. var=${USER:=value}  
The assignment (:=) operator is used to assign a value to the variable if it doesn’t already have one. Try the following examples: 
# echo ${TEST} // Variable TEST is not assigned yet

# echo ${TEST:=foo} // Setup TEST as 'foo'
foo
# echo ${TEST}
foo
# echo ${TEST:=abc} // TEST is set already. So keep original value
foo
# unset TEST
# echo ${TEST} 

Tip: ${var:-defaultValue} vs ${var:=defaultValue} 
Please note that it will not work with positional parameter arguments: 
  1. var=${1:=defaultValue}  ### FAIL with an error cannot assign in this way  
  2. var=${1:-defaultValue}    ### Perfect  
#2: Display an Error Message If $VAR Not Passed 
If the variable is not defined or not passed, you can stop executing the Bash script with the following syntax: 
  1. ${varName?Error varName is not defined}  
  2. ${varName:?Error varName is not defined or is empty}  
  3. ${1:?"mkjail: Missing operand"}  
  4. MESSAGE="Usage: mkjail.sh domainname IPv4"             ### define error message  
  5. _domain=${2?"Error: ${MESSAGE}"}  ### you can use $MESSAGE too  
This is used for giving an error message for unset parameters. In this example, if the $1 command line arg is not passed, stop executing the script with an error message: 
  1. _domain="${1:?Usage: mknginxconf domainName}"  
Here is a sample script: 
  1. #!/bin/bash  
  2. # Purpose: Wrapper script to setup Nginx Load Balancer   
  3. # Author: Vivek Gite   
  4. _root="/nas.pro/prod/scripts/perl/nginx"  
  5. _setup="${_root}/createnginxconf.pl"  
  6. _db="${_root}/database/text/vips.db"  
  7. _domain="${1:?Usage: mknginxconf domainName}"     ### die if domainName is not passed ####  
  8.   
  9. [ ! -f $_db ] && { echo "$0: Error $_db file not found."; exit 1; }  
  10. line=$(grep "^${_domain}" $_db) || { echo "$0: Error $_domain not found in $_db."; exit 2; }  
  11.   
  12. # Get domain config info into 4 fields:    
  13. # f1 - Domain Name|  
  14. # f2 - IPv4Vip:httpPort:HttpsPort, IPv6Vip:httpPort:HttpsPort|  
  15. # f3 - PrivateIP1:port1,PrivateIP2,port2,...PrivateIPN,portN|  
  16. # f4 - LB Type (true [round robin] OR false [session])  
  17. # -------------------------------------------------------------------------------  
  18. IFS='|'  
  19. read -r f1 f2 f3 f4  <<<"$line"  
  20.   
  21. # Do we want ssl host config too?  
  22. IFS=':'  
  23. set -- $f2  
  24. ssl="false"  
  25. "$3" == "443" ] && ssl="true"  
  26.   
  27. # Build query   
  28. d="$f1:$ssl:$f4"  
  29. IFS=','  
  30. ips=$f3  
  31.   
  32. # Call our master script to setup nginx reverse proxy / load balancer (LB) for given domain name  
  33. $_setup "$d" $ips  
#2.1: Display an Error Message and Run Command 
If $2 is not set display an error message for $2 parameter and run cp command on fly as follows: 
  1. #!/bin/bash  
  2. _file="$HOME/.input"  
  3. _message="Usage: chkfile commandname"  
  4.   
  5. # Run another command (compact format)  
  6. _cmd="${2:? $_message $(cp $_file $HOME/.output)}"  
  7.   
  8. $_cmd "$_file"  
#3: Find Variable Length 
You can easily find string length using the following syntax: 
  1. ${#variableName}  
  2. echo ${#variableName}  
  3. len=${#var}  
Here is a sample shell script to add a ftp user: 
  1. #!/bin/bash  
  2. # Usage : Add a ftp user  
  3. _fuser="$1"  
  4. _fpass="$2"  
  5.   
  6. # die if username/password not provided   
  7. [ $# -ne 2 ] && { echo "Usage: addftpuser username password"; exit 1;}  
  8.   
  9. # Get username length and make sure it always <= 8  
  10. [[ ${#_fuser} -ge 9 ]] && { echo "Error: Username should be maximum 8 characters in length. "; exit 2;}  
  11.   
  12. # Check for existing user in /etc/passwd   
  13. /usr/bin/getent passwd "${_fuser}" &>/dev/null  
  14.   
  15. # Check exit status  
  16. [ $? -eq 0 ] && { echo "Error: FTP username \"${_fuser}\" exists."; exit 3; }  
  17.   
  18. # Add user   
  19. /sbin/useradd -s /sbin/nologin -m  "${_fuser}"  
  20. echo "${_fpass}" | /usr/bin/passwd "${_fuser}" --stdin  
Each Linux or UNIX command returns a status when it terminates normally or abnormally. You can use command exit status in the shell script to display an error message or take some sort of action. In above example, if getent command is successful, it returns a code which tells the shell script to display an error message. 0 exit status means the command was successful without any errors. $? holds the return value set by the previously executed command. 

#4: Remove Pattern (Front of $VAR) 
The syntax is as follows: 
  1. ${var#Pattern}            
  2. ${var##Pattern}   
You can strip $var as per given pattern from front of $var. In this example remove /etc/ part and get a filename only, enter: 
  1. f="/etc/resolv.conf"  
  2. echo ${f#/etc/}  
The first syntax removes shortest part of pattern and the second syntax removes the longest part of the pattern. Consider the following example: 
  1. _version="20090128"  
  2. _url="http://dns.measurement-factory.com/tools/dnstop/src/dnstop-${_version}.tar.gz"  
You just want to get filename i.e. dnstop-20090128.tar.gz, enter (try to remove longest part of $_url) : 
# echo "${_url##*/}"
dnstop-20090128.tar.gz
Now try using the longest part of the pattern syntax: 
# echo "${_url#*/}"
/dns.measurement-factory.com/tools/dnstop/src/dnstop-20090128.tar.gz

This is also useful to get a script name without using /bin/basename
  1. #!/bin/bash  
  2. _self="${0##*/}"  
  3. echo "$_self is called"  
Create a script called master.info as follows: 
  1. #!/bin/bash  
  2. # Purpose: Display jail info as per softlink  
  3. # Author: Vivek Gite  
  4. _j="$@"  
  5.   
  6. # find out script name  
  7. _self="${0##*/}"  
  8.   
  9. "$VERBOSE" == "1" ] && echo "Called as $_self for \"$_j\" domain(s)"  
  10.   
  11. for j in $_j  
  12. do  
  13.     export _DOMAIN_NAME=$j  
  14.     source functions.sh  
  15.     init_jail  
  16.         # call appropriate functions as per script-name / softlink   
  17.     case $_self in  
  18.         uploaddir.info) echo "Upload dir for $j: $(get_domain_upload_dir)" ;;  
  19.         tmpdir.info) echo "/tmp dir for $j: $(get_domain_tmp_dir)" ;;  
  20.                 mem.info) echo "$j domain mem usage (php+lighttpd): $(get_domain_mem_info)" ;;  
  21.                 cpu.info) echo "$j domain cpu usage (php+lighttpd): $(get_domain_cpu_info)" ;;  
  22.                 user.info) echo "$j domain user and group info: $(get_domain_users_info)" ;;  
  23.                 diskquota.info) echo "$j domain disk quota info (mysql+disk): $(get_domain_diskquota_info)" ;;  
  24.         *) warn "Usage: $_self"  
  25.     esac  
  26. done  
Finally, create softlink as follows: 
# ln -s master.info uploaddir.info
# ln -s master.info tmpdir.info
# ln -s master.info mem.info
....
..
You can now call script as follows: 
# ./mem.info example.org
# ./cpu.info example.com example.net

#4.1: Remove Pattern (Back of $VAR) 
The syntax is as follows: 
  1. ${var%pattern}  
  2. ${var%%pattern}  
Exactly the same as above, except that it applies to the back of $var. In this example remove .tar.gz from $FILE, enter: 
# export FILE="xcache-1.3.0.tar.gz"
# echo ${FILE%.tar.gz}
xcache-1.3.0

Rename all *.perl files to *.pl using bash for loop as Apache web server is configured to only use .pl file and not .perl file names: 
  1. for p in /scripts/projects/.devl/perl/*.perl  
  2. do  
  3.     mv "$p" "${p%.perl}.pl"  
  4. done  
You can combine all of them as follows to create a build scripts: 
  1. #!/bin/bash  
  2. # Usage: Build suhosin module for RHEL based servers  
  3. # Author: Vivek Gite  
  4. # ----  
  5. # Set default value for $2   
  6. VERSION="-${2:-0.9.31}"  
  7. URL="http://download.suhosin.org/suhosin${VERSION}.tgz"  
  8. vURL="http://download.suhosin.org/suhosin${VERSION}.tgz.sig"  
  9.   
  10. # Get tar ball names  
  11. FILE="${URL##*/}"  
  12. vFILE="${vURL##*/}"  
  13. DLHOME="/opt"  
  14. SOFTWARE="suhosin"  
  15.   
  16. # Remove .tgz and get dir name  
  17. DEST="${FILE%.tgz}"  
  18.   
  19. # Download software  
  20. wget $URL -O "${DLHOME}/$FILE"  
  21. wget $vURL -O "${DLHOME}/$vFILE"  
  22.   
  23. # Extract it  
  24. tar -zxvf $FILE  
  25. cd "$DEST"  
  26.   
  27. # Build it and install it  
  28. phpize --clean && phpize && ./configure && make && read -p "Update/Install $SOFTWARE [Y/n] ? " answer  
  29. shopt -s nocasematch  
  30. [[ $answer =~ y|es  ]] && make install  
  31. shopt -u nocasematch  
If you turn on nocasematch option, shell matches patterns in a case-insensitive fashion when performing matching while executing case or [[ conditional expression

#5: Find And Replace 
The syntax is as follows: 
  1. ${varName/Pattern/Replacement}  
  2. ${varName/word1/word2}  
  3. ${os/Unix/Linux}  
Find word unix and replace with linux, enter: 
# export X='Use unix or die'
# sed 's/unix/linux/' <<< $X
Use linux or die
You can avoid using sed as follows: 
# echo "${X/unix/linux}"
Use linux or die
# OUT="${X/unix/linux}"
# echo "${OUT}"
Use linux or die

To replace all matches of pattern, enter : 
  1. out="${x//unix/linux}"  
You can use this to rename or remove files on fly 
  1. y=/etc/resolv.conf  
  2. cp "${y}" "${y/.conf/.conf.bak}"  
Here is another example: 
  1.        # RHEL php modules path  
  2. _php_modules="/usr/lib64/php/modules"  
  3. for i in $_php_modules/* 
  4. do 
  5.     p="${i##*/}"                  ## Get module name  
  6.     ini="/etc/php.d/${p/so/ini}"  ## Get ini file by replacing .so with .ini extension   
  7.                # make sure file exists   
  8.     [ ! -f "$ini" ] && echo "$i php module exists but $ini file not found."  
  9. done  
The following function installs required modules in chrooted php-cgi process 
  1. install_php_modules(){  
  2.         # get jail name  
  3.     local n="${_chrootbase}/${d##/}"  
  4.     local p=""  
  5.     local ini=""  
  6.         # enable only ${_php_modules_enabled} php modules and delete other .ini files if exists in jail  
  7.     for i in $_php_modules/* 
  8.     do 
  9.         p="${i##*/}"  
  10.         ini="$n/etc/php.d/${p/so/ini}"  
  11.                 # find out if module is enabled or not  
  12.         if [[ ${_php_modules_enabled} = *${p}*   ]]  
  13.         then  
  14.             [ "$VERBOSE" == "1" ] && echo " [+] Enabling php module $p"  
  15.             $_cp -f "$i" "$n/${_php_modules##/}"      ## install it  
  16.             copy_shared_libs "$i"                     ## get shared libs in jail too  
  17.         else  
  18.             [ -f "${ini}" ] && $_rm -f "${ini}"   ## if old .ini exists in jail, just delete it  
  19.         fi  
  20.     done  
  21. }  
#6: Substring Starting Character 
The syntax is as follows: 
  1. ${parameter:offset}  
  2. ${parameter:offset:length}  
  3. ${variable:position}  
  4. var=${string:position}   
Expands to up to length characters of parameter starting at the character specified by offset
# export base='/backup/nas'
# export file='/data.tar.gz'
# echo '${base}/${file:1}'
${base}/${file:1}
# echo "${base}/${file:1}"
/backup/nas/data.tar.gz

Below will extract word 'craft' only: 
  1. x="nixcraft.com"  
  2. echo ${x:3:5}"  
Summary: String Manipulation and Expanding Variables 
For your ready references here are all your handy bash parameter substitution operators. Try them all; enhance your scripting skills like a pro: 

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...