Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

cmdparser - parse command line arguments with Bash built-in regex matching

Author: jv
License: The MIT License, Copyright (c) 2009 jv
Description: a basic regex-based command line parser for use in Bash scripts (Mac OS X); uses Bash built-in regex matching via the comparison operator "=~" and the array variable BASH_REMATCH; an alternative to the built-in getopts command (cf. help getopts); use at your own risk
Bash version: GNU bash, version 3.2.25(1)-release
Usage: /path/to/script_with_cmdparser -a -b -c -f file

Related links:
- cmdparser - parse command line arguments (links)
- Process positional parameters non-destructively in Bash
- Update your Bash shell via MacPorts

Version 1: modifies (the number of) command line arguments ($# and $@).

#!/opt/local/bin/bash

# bash --version
# GNU bash, version 3.2.25(1)-release

# This version of cmdparser uses Bash built-in regular expression matching and 
# modifies (the number of) command line arguments ($# and $@).


export PATH=/usr/bin:/bin:/usr/sbin:/sbin
export IFS=$' \t\n'

# create a fake command line
#set -- -abcc -c -zz -flag1="" -flag2=arg -flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' -flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces  / * + ` \ ! ' -flag9 ~/Desktop/*.txt filename1 filename2 filename3

set -- -abcc -c -zz -flag1="" -flag2=arg -flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' -flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ ! ' -flag9 '~/Desktop/*.txt' filename1 filename2 filename3


printf "%s\n" "$@" | nl
#printf "%s" "$@"$'\n' | nl
#printf "%s" "${@/%/ }" | nl


: <<-'COMMENT'

# copy & paste examples for the command line

echo "filename1" "filename2" "filename3" | ~/Desktop/cmdparser.txt -abcc -c -zz -flag1 arg -flag2=arg --flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' --flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ ! ' -flag9 ~/Desktop/*.txt -

echo "filename1" "filename2" "filename3" | ~/Desktop/cmdparser.txt -abcc -c -zz -flag1 arg -flag2=arg --flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' --flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ ! ' -flag9 '~/Desktop/*.txt' -

COMMENT



# cmdparser

usage="usage: $(/usr/bin/basename "$0") [-a] [-b] [-c] [-cc] [-zz] [-flag1 arg] [-flag2 'arg1 arg2 ...'] [-flag3=arg] [-flag4=\"arg1 arg2\"] ..."


# Note: names of flags and switches may not contain the characters "-", "." or "=".

# define the names of flags as a regular expression
# flags are command line options that require arguments

flags="(flag1|flag2|flag3|flag4|flag5|flag6|flag7|flag8|flag9)"


# define the names of switches as a regular expression
# Switches are command line options that do not take arguments.
# Make sure multi-char switches precede single-char switches in the regular expression.
# Note that the regular expression contains neither the special read-from-stdin switch "-" 
# nor the special end-of-options switch "--".

switches="(cc|zz|a|b|c)"  


declare flag1 flag2 flag3 flag4 flag5 flag6 flag7 flag8 flag9                # flags
declare -i a=0 b=0 c=0 cc=0 zz=0                                            # switches
                         
declare argstr argvar argvar_escaped char flagvar optstr piped pipedstr       # script variables
declare -i optid pipedvar

# piped="piped" will be used for variable creation 
# example: piped="piped"; pipedstr="piped arg"; eval $piped='"$(echo "$pipedstr")"'; echo "$piped"

piped="piped"

# default value is set to "no pipe"
pipedvar=0
pipedstr=""

# if /dev/stdin has a size greater than zero ...
if [[ -s /dev/stdin ]]; then pipedstr="$(</dev/stdin)"; fi 

if [[ $# -eq 0 ]] && [[ -z "$pipedstr" ]]; then
  printf "\n%s\n\n%s\n\n" 'No arguments specified!' "$usage" 1>&2
  exit 1
fi 

if [[ $# -eq 0 ]] && [[ -n "$pipedstr" ]]; then
  eval $piped='"${pipedstr}"'  
  pipedvar=1
fi 

# if there are command line arguments ...
# Note that $pipedvar may still be set to 1 below if the special read-from-stdin switch "-" is given.

if [[ $pipedvar -eq 0 ]]; then

   optstr=" "  
   optid=0

   while [[ -n "$optstr" ]]; do     

      # try to extract valid flags or switches from positional parameter $1
      # $1 gets shifted afterwards (cf. help shift)


      [[ "$1" =~ ^--?${flags}$ ]]
      optstr="${BASH_REMATCH[0]}"


      if [[ -n "$optstr" ]]; then optid=1; fi

      if [[ -z "$optstr" ]]; then optid=2;  [[ "$1" =~ ^--?${switches}$ ]]; optstr="${BASH_REMATCH[0]}"; fi

      if [[ -z "$optstr" ]]; then optid=3; [[ "$1" =~ ^--?${switches}+$ ]]; optstr="${BASH_REMATCH[0]}"; fi

      if [[ -z "$optstr" ]]; then optid=4; [[ "$1" =~ ^--?(${flags}=.*|${flags}[^[:space:]]+)$ ]]; optstr="${BASH_REMATCH[0]}"; fi


      if [[ -z "$optstr" ]]; then  
         if [[ "$1" = "-" ]] && [[ "$@" = "-" ]]; then  
            optid=5
            optstr="-" 
         fi
      fi


      if [[ -z "$optstr" ]]; then

         # append a space to each command line argument
         argstr="$(printf "%s" "${@/%/ }")"

         [[ "$argstr" =~ [[:space:]]--?(${flags}|${switches}) ]]

         if [[ -n "${BASH_REMATCH[0]}" ]]; then 
            printf "\n%s\x21\n\n%s\n\n%s\n\n" "Undefined non-option string: ${1:0:1000} is followed by a legal flag or switch" "${BASH_REMATCH[0]}" "$usage" 1>&2
            exit 1
         fi

      fi


      if [[ "$1" = "--" ]]; then shift; break; fi     # -- marks end of options

      if [[ -z "$optstr" ]]; then break; fi     # no further flags or switches to process


      # flag followed by space (example: -f file)
      if [[ $optid -eq 1 ]]; then 

         if [[ -z "$2" ]]; then
            printf "%s\n%s\n" "no argument given to flag: ${1}" "$usage" 1>&2
            exit 1
         fi 

         flagvar="${1#"${1%%[!-]*}"}"     # remove leading - or --
         argvar="$2"
         eval $flagvar='"${argvar}"'
         shift 2     # shift positional parameters $1 & $2 (that is, a flag plus its argument)
         continue


      # single switch (example: -a)
      elif [[ $optid -eq 2 ]]; then
         flagvar="${1#"${1%%[!-]*}"}"
         eval $flagvar='"1"'
         shift
         continue
  

      # combined switch (example: -abcc)
      elif [[ $optid -eq 3 ]]; then

         flagvar="${1#"${1%%[!-]*}"}"

         while [[ -n "$flagvar" ]]; do

            [[ "$flagvar" =~ ^${switches}.*$ ]]
            char="${BASH_REMATCH[1]}"

            eval $char='"1"'

            [[ "$flagvar" =~ ^${switches}(.*)$ ]]
            flagvar="${BASH_REMATCH[2]}"

         done

         shift
         continue


      # flag without following space (example: -ffile)
      elif [[ $optid -eq 4 ]]; then 

         [[ "${1#"${1%%[!-]*}"}" =~ ^${flags}=?(.*)$ ]]
         argvar="${BASH_REMATCH[2]}"

         [[ "${1#"${1%%[!-]*}"}" =~ ^${flags}=?.*$ ]]
         flagvar="${BASH_REMATCH[1]}"

         # alternative
         #[[ "${1}" =~ ^--?${flags}=?(.*)$ ]]
         #argvar="${BASH_REMATCH[2]}"

         #[[ "${1}" =~ ^--?${flags}=?.*$ ]]
         #flagvar="${BASH_REMATCH[1]}"

         eval $flagvar='"${argvar}"'
         shift
         continue


      # the special read-from-stdin switch "-"
      elif [[ $optid -eq 5 ]]; then 
         pipedvar=1
         eval $piped='"${pipedstr}"'
         shift
         break

      fi

      # remove positional parameter $1 from "$@"
      shift

   done

fi   # if [[$pipedvar -eq 0 ]]; then ...


echo 

printf "%s\t%s\n" "a:" "${a}"
printf "%s\t%s\n" "b:" "${b}"
printf "%s\t%s\n" "c:" "${c}"
printf "%s\t%s\n" "cc:" "${cc}"
printf "%s\t%s\n" "zz:" "${zz}"
printf "%s\t%s\n" "flag1:" "${flag1}"
printf "%s\t%s\n" "flag2:" "${flag2}"
printf "%s\t%s\n" "flag3:" "${flag3}"
printf "%s\t%s\n" "flag4:" "${flag4}"
printf "%s\t%s\n" "flag5:" "${flag5}"
printf "%s\t%s\n" "flag6:" "${flag6}"
printf "%s\t%s\n" "flag7:" "${flag7}"
printf "%s\t%s\n" "flag8:" "${flag8}"
printf "%s\t%s\n" "flag9:" "${flag9}"

echo


if [[ $pipedvar -eq 1 ]] && [[ -z "$@" ]]; then 
   echo "remaining string-piped: ${piped}"
else 
   echo "remaining string: ${@}"
fi

echo

if [[ $flag9 == '~/Desktop/*.txt' ]]; then printf "%s\n" ~/Desktop/*.txt | nl; fi

echo

exit 0




Version 2: does not modify (the number of) command line arguments ($# and $@).

#!/opt/local/bin/bash

# bash --version
# GNU bash, version 3.2.25(1)-release

# This version of cmdparser uses Bash builtin regex matching and 
# does not modify (the number of) command line arguments ($# and $@).


# create a fake command line
set -- -abcc -c -zz -flag1="" -flag2=arg$'\n'plus_newline -flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' -flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ !' -flag9 ~/Desktop/*.txt filename1 filename2 filename3

#set -- -abcc -c -zz -flag1="" -flag2=arg$'\n'plus_newline -flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' -flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ !' -flag9 '~/Desktop/*.txt' filename1 filename2 filename3


printf "%s\n" "$@" | nl
#printf "%s" "$@"$'\n' | nl
#printf "%s" "${@/%/ }" | nl


: <<-'COMMENT'

# copy & paste examples

echo "filename1" "filename2" "filename3" | ~/Downloads/Mac-OS-X-bash-scripts/bash-cmdparser/cmdparser-non-destructive-1.txt -abcc -c -zz -flag1 arg -flag2=arg$'\n'plus_newline --flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' --flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ !' -flag9 ~/Desktop/*.txt -

echo "filename1" "filename2" "filename3" | ~/Downloads/Mac-OS-X-bash-scripts/bash-cmdparser/cmdparser-non-destructive-1.txt -abcc -c -zz -flag1 arg -flag2=arg$'\n'plus_newline --flag3="arg" -flag4='arg1=*,arg2=?,arg3=!' -flag5 '(arg1|arg2|arg3)' -flag6 'arg1=ag,arg2=bg,arg3=cg' --flag7 An\ argument\ with\ spaces\! -flag8='Yet another argument with spaces / * + ` \ !' -flag9 '~/Desktop/*.txt' -

COMMENT


echo

echo "Number of positional parameters: ${#}"

echo


# cmdparser

export PATH=/usr/bin:/bin:/usr/sbin:/sbin
export IFS=$' \t\n'


# Note: names of flags and switches may not contain the characters "-", "." or "=".

# define the names of flags as a regular expression
# flags are command line options that require arguments

flags="(flag1|flag2|flag3|flag4|flag5|flag6|flag7|flag8|flag9)"


# define the names of switches as a regular expression
# Switches are command line options that do not take arguments.
# Make sure multi-char switches precede single-char switches in the regular expression.
# Note that the regular expression contains neither the special read-from-stdin switch "-" 
# nor the special end-of-options switch "--".

switches="(cc|zz|a|b|c)"  


usage="usage: $(/usr/bin/basename "$0") [-a] [-b] [-c] [-cc] [-zz] [-flag1 arg] [-flag2 'arg1 arg2 ...'] [-flag3=arg] [-flag4=\"arg1 arg2\"] ..."

declare flag1 flag2 flag3 flag4 flag5 flag6 flag7 flag8 flag9                # flags
declare -i a=0 b=0 c=0 cc=0 zz=0                                            # switches
                         
declare argn argstr argvar argvar_escaped char flagvar optstr piped pipedstr       # script variables
declare -i optid pipedvar
declare -a argarslice

# piped="piped" will be used for variable creation 
# example: piped="piped"; pipedstr="piped arg"; eval $piped='"$(echo "$pipedstr")"'; echo "$piped"

piped="piped"

# default value is set to "no pipe"
pipedvar=0
pipedstr=""

# if /dev/stdin has a size greater than zero ...
if [[ -s /dev/stdin ]]; then pipedstr="$(</dev/stdin)"; fi 

if [[ $# -eq 0 ]] && [[ -z "$pipedstr" ]]; then
  printf "\n%s\n\n%s\n\n" 'No arguments specified!' "$usage" 1>&2
  exit 1
fi 

if [[ $# -eq 0 ]] && [[ -n "$pipedstr" ]]; then
  eval $piped='"${pipedstr}"'  
  pipedvar=1
fi 

# if there are command line arguments ...
# Note that $pipedvar may still be set to 1 below if the special read-from-stdin switch "-" is given

if [[ $pipedvar -eq 0 ]]; then

   optstr=" "  
   optid=0


   # processing one positional parameter at a time without modifying $# or $@
   # Process positional parameters non-destructively in Bash, http://codesnippets.joyent.com/posts/show/1706

   for (( i=1; i <= $#; i++ )); do 


      argn="${@:${i}:1}"     # current positional parameter
                             # "${@:(${i}+1):1}": the positional parameter following the current one
                             # "${@:${i}}": all positional parameters starting with the current one


      [[ "$argn" =~ ^--?${flags}$ ]]
      optstr="${BASH_REMATCH[0]}"


      if [[ -n "$optstr" ]]; then optid=1; fi

      if [[ -z "$optstr" ]]; then optid=2;  [[ "$argn" =~ ^--?${switches}$ ]]; optstr="${BASH_REMATCH[0]}"; fi

      if [[ -z "$optstr" ]]; then optid=3; [[ "$argn" =~ ^--?${switches}+$ ]]; optstr="${BASH_REMATCH[0]}"; fi

      if [[ -z "$optstr" ]]; then optid=4; [[ "$argn" =~ ^--?(${flags}=.*|${flags}[^[:space:]]+)$ ]]; optstr="${BASH_REMATCH[0]}"; fi


      if [[ -z "$optstr" ]]; then  
         if [[ "${argn}" = "-" ]] && [[ "${@:${i}}" = "-" ]]; then
            optid=5
            optstr="-" 
         fi
      fi


      if [[ -z "$optstr" ]]; then

         # append a space to each command line argument
         argarslice=( "${@:${i}}" )
         argstr="$(printf "%s" "${argarslice[@]/%/ }")"

         [[ "$argstr" =~ [[:space:]]--?(${flags}|${switches}) ]]

         if [[ -n "${BASH_REMATCH[0]}" ]]; then 
            printf "\n%s\x21\n\n%s\n\n%s\n\n" "Undefined non-option string: ${argn:0:1000} is followed by a legal flag or switch" "${BASH_REMATCH[0]}" "$usage" 1>&2
            exit 1
         fi

      fi


      if [[ "${argn}" = "--" ]]; then break; fi     # -- marks end of options

      if [[ -z "$optstr" ]]; then break; fi     # no further flags or switches to process


      # flag followed by space (example: -f file)
      if [[ $optid -eq 1 ]]; then 

         if [[ -z "${@:(${i}+1):1}" ]]; then
            printf "%s\n%s\n" "no argument given to flag: ${argn}" "$usage" 1>&2
            exit 1
         fi 

         flagvar="${argn#"${argn%%[!-]*}"}"     # remove leading dashes
         argvar="${@:(${i}+1):1}"
         eval $flagvar='"${argvar}"'
         let "i += 1"     # skip argument of current flag in next for loop
         continue

      # single switch (example: -a)
      elif [[ $optid -eq 2 ]]; then
         flagvar="${argn#"${argn%%[!-]*}"}"
         eval $flagvar='"1"'
         continue
  
      # combined switch (example: -abcc)
      elif [[ $optid -eq 3 ]]; then
         flagvar="${argn#"${argn%%[!-]*}"}"
         while [[ -n "$flagvar" ]]; do

            [[ "$flagvar" =~ ^${switches}.*$ ]]
            char="${BASH_REMATCH[1]}"

            eval $char='"1"'

            [[ "$flagvar" =~ ^${switches}(.*)$ ]]
            flagvar="${BASH_REMATCH[2]}"

         done
         continue


      # flag without following space (example: -ffile)
      elif [[ $optid -eq 4 ]]; then 

         [[ "${argn#"${argn%%[!-]*}"}" =~ ^${flags}=?(.*)$ ]]
         argvar="${BASH_REMATCH[2]}"

         [[ "${argn#"${argn%%[!-]*}"}" =~ ^${flags}=?.*$ ]]
         flagvar="${BASH_REMATCH[1]}"

         # alternative
         #[[ "${argn}" =~ ^--?${flags}=?(.*)$ ]]
         #argvar="${BASH_REMATCH[2]}"

         #[[ "${argn}" =~ ^--?${flags}=?.*$ ]]
         #flagvar="${BASH_REMATCH[1]}"


         eval $flagvar='"${argvar}"'
         continue


      # the special read-from-stdin switch "-"
      elif [[ $optid -eq 5 ]]; then 
         pipedvar=1
         eval $piped='"${pipedstr}"'
         break

      fi

   done   # for loop

fi   # if [[$pipedvar -eq 0 ]]; then ...


echo 

printf "%s\t%s\n" "a:" "${a}"
printf "%s\t%s\n" "b:" "${b}"
printf "%s\t%s\n" "c:" "${c}"
printf "%s\t%s\n" "cc:" "${cc}"
printf "%s\t%s\n" "zz:" "${zz}"
printf "%s\t%s\n" "flag1:" "${flag1}"
printf "%s\t%s\n" "flag2:" "${flag2}"
printf "%s\t%s\n" "flag3:" "${flag3}"
printf "%s\t%s\n" "flag4:" "${flag4}"
printf "%s\t%s\n" "flag5:" "${flag5}"
printf "%s\t%s\n" "flag6:" "${flag6}"
printf "%s\t%s\n" "flag7:" "${flag7}"
printf "%s\t%s\n" "flag8:" "${flag8}"
printf "%s\t%s\n" "flag9:" "${flag9}"

echo


if [[ $pipedvar -eq 1 ]] && [[ -z "$@" ]]; then 
   echo "remaining string-piped: ${piped}"
else 
   echo "remaining string: ${@}"
fi

echo

echo "Number of positional parameters: ${#}"

echo

if [[ $flag9 == '~/Desktop/*.txt' ]]; then printf "%s\n" ~/Desktop/*.txt | nl; fi

#if [[ $flag9 == '~/Desktop/*.txt' ]]; then printf "%s\n" ${flag9} | nl; fi

echo

exit 0



VB.net Modified Preorder Tree Traversal

In order to utilize this, I would recommend reading the article located at sitepoint.com (Search google for modified preorder tree traversal). It took me quite some time and some help from a coworker to port this from PHP to VB.net. :D

Replace the application("sql").query("*") with your SQL code, I was inclined to write my own SQL class to make querying DB's easier, but you can do whatever you want.
record = application("sql").query("select * from categories order by lft asc")
dim output as new arraylist()
dim right as new arraylist()
do while not record.eof
  if right.count > 0
    do while right(right.count - 1) < record("rgt").value
      right.removeAt(right.count - 1)
      if right.count = 0 then exit do
    loop
  end if
  response.write(record("name").value & " is " & right.count & " levels deep in the tree..." & controlchars.cr)
  right.add(record("rgt").value)
  record.movenext
loop

Net::HTTP with timeout support http and https and basic auth

This takes a username, password, url and optional time out

require 'net/http'
require 'net/https'
#Usage: username pass urlStr time_out
#

    urlStr = 'http://localhost:3000/cron/cron'
    username = "badname"
    pass = "badpass"
    time_out = 60

    if ARGV[3] != nil
     time_out = ARGV[3].to_i
    end

    if ARGV[2] != nil
     urlStr = ARGV[2]
    end
    
    if ARGV[1] != nil and ARGV[0] != nil
     username = ARGV[0]
     pass = ARGV[1]
    end
    puts urlStr + " user: "+username
    
    url = URI.parse(urlStr)
    use_ssl = url.scheme == 'https'
    req = Net::HTTP::Get.new url.path
    req.basic_auth username, pass
 
    http = Net::HTTP.new(url.host, url.port)
    http.read_timeout=time_out
    if use_ssl
      http.use_ssl = true
    end
    res = http.start { |web| 
      web.request(req) 
    }
    
    puts res.body