Et eksempel på avansert Bash-skripting

Flere av programmene som finnes for Linux er skrevet i et skriptspråk. Hvis du starter et terminalvindu i Linux for å komme til kommandolinja så finner du Bash. Der kan skrive kommandoer. Terminalen, eller kommandolinjevinduet kan også kalles for gnome-terminal eller konsole. Her er et kommentert bash-skript som demonstrerer bruk av noen spesielle teknikker.

Skriptet tar for seg følgende tema:

  • Håndtering av opsjon med og uten argument, se linjene 57-71 for et manuellt eksempel og linjene 121-129 hvor den for bash innebygde getopts brukes.
  • Bruk av regulæruttrykk i Bash for å sjekke variabler, se linjer 83 og 84.
  • Substitusjon av innhold i variabler, se linjer 73,74 og 87.
  • Definisjon av helt nye variabler ved bruk av eval, se linjene 89 og 98.
  • Bruk av: bash -c 'skript' argumenter..., se linjene 43-138.

Beskrivelse av eksempelskriptet:
Eksempelet skal støtte opsjonsflaggene: -h og -v som vanligvis betyr at programmet skal skrive hjelp og versjonsnummeret. Videre så er det spesifisert at brukeren skal kunne definere sine egne variabler på kommandolinja ved hjelp av opsjonen: -D variabel=verdi eller -Dvariabel=verdi.

Eksempelskriptet:

     1	#!/usr/bin/env bash
       
     2	if [ "$1" = "-h" ]; then
     3	    <<EOF cat 
     4	progname -- demonstrate advanced Bash techniques
     5	Usage: bash thisfile [-D variable=value] [-hv] [--] argument...
     6	  -h Show help
     7	  -v Show version
     8	  -- No options after this
     9	  argument...
    10	     One or more arguments
       
    11	This script demonstrates how to:
    12	- handle options, with or without arguments. See
    13	  lines 57-71 for manual method and lines 121-129 
    14	  where using getopts,  builtin.
    15	- use regular expressions within Bash, lines 83 and 84.
    16	- substitute within variables, lines 73,74 and 87.
    17	- define new variables on the fly using 'eval', 
    18	  lines 89 and 98. 
    19	- implement a script using: bash -c '<a script>' ...
    20	  lines 43-138.
    21	EOF
    22	    exit 0
    23	fi
       
       
    24	# Set a default a command line if none is provided,
    25	# see:   help set
    26	if [ "$#" -eq 0 ]; then
    27	    set -- -DDEBUG=1                           \
    28	           -Dvar1="value1"                     \
    29	           -Dvar2="A single quote: ' cost \$0" \
    30	           -hv                                 \
    31	           --   arg1   "arg2 arg2"   arg3
    32	fi
       
    33	echo "INITIAL COMMAND LINE BEFORE bash -c ... , lines 33-41:"
    34	echo 
    35	let i=0
    36	echo "\$0:" $0
    37	for arg in "$@"
    38	do
    39	    let i=i+1
    40	    echo "\$$i:" $arg
    41	done
       
    42	# see  'man bash'
    43	bash -c '
       
    44	# Obtain and evaluate assignments, rebuild the command line 
    45	# Define variables: SQ=single quote and NL=newline
    46	SQ="'\''" 
    47	NL="
    48	"
    49	DEBUG=1   # 0: no debug / 1: debug
       
    50	# Extract assignments from command line:  
    51	#         -D variable=value or -Dvariable=value
    52	let i=0
    53	ASSIGNMENTS=""
    54	while [ "$#" -gt 0 ] 
    55	do 
    56	    assignment=""
    57	    case "$1" in
    58		-D) # -D variable=value
    59		    shift
    60		    if [ $# -gt 0 ]; then
    61			assignment=$1
    62		    else
    63			echo "error: assignment expected after -D" 1>&2
    64			exit 1
    65		    fi;;
    66		-D*)# -Dvariable=value
    67		    assignment=${1:2};; # everything after the initial -D
    68		*)  # another argument
    69		    ARGV[i]=$1 # shall use this to rebuild the command line
    70		    let i=i+1;;      
    71	    esac
    72	    if [ -n "$assignment" ]; then
    73	        # Extract lvalue and rvalue from assignment string
    74	        lvalue=${assignment/%=*/}
    75	        rvalue=${assignment/#*=/}
       
    76	        # For safety reasons, only assignments must be evaluated. 
    77	        # Therefore:
    78	        # - use regex to qualify the lvalue as good, examples:
    79	        #   name, _name2 and array[2]
    80	        # - the rvalue must be protected by single quotes, however,
    81	        #   existing single quotes must be escaped first
       
    82	        # This demonstrates using extended regex in Bash
    83	        regex_goodlvalue="^[_a-zA-Z][_a-zA-Z0-9]*([[] *[0-9]+ *[]])?$" 
    84	        if [[ "$lvalue" =~ $regex_goodlvalue ]]
    85	        then
    86	            # - escape single quotes of rvalue
    87	            # - buffer the assignments: lvalue=rvalue;
    88	            rvalue="${rvalue//$SQ/$SQ\\$SQ$SQ}"  
    89		    ASSIGNMENTS="$ASSIGNMENTS$lvalue=$SQ$rvalue$SQ;$NL"
    90	        else
    91	            echo "error: bad variable name: $lvalue" 1>&2
    92	            exit 1
    93	        fi
    94	    fi
    95	    shift
    96	done
    97	# Evaluate all assignments defined by -Dvariable=value
    98	eval "$ASSIGNMENTS"  
    99	# but, since we are not done, define the unprocessed command line: $@
   100	set -- "${ARGV[@]}"  
       
   101	if [ "$DEBUG" -eq 1 ]; then
       
   102	      <<EOF cat # write debug information using a here document
   103	-----------------------------------------
   104	WITHIN bash -c ... -- SHOW EVALUATED ASSIGNMENTS, lines 98, 102-106:
   105	$ASSIGNMENTS
       
   106	EOF
   107	      echo "Show the defined value of ${SQ}var1${SQ}: $var1"
   108	      echo "Show the defined value of ${SQ}var2${SQ}: $var2"
   109	      echo 
   110	 
   111	      echo "WITHIN bash -c ...  -- SHOW REMAINING COMMAND LINE, lines 111-117:"
   112	      let i=1
   113	      for arg in "$@"
   114	      do
   115	          echo "\$$i: " $arg
   116	          let i=i+1
   117	      done
   118	      echo "-----------------------------------------"
   119	fi
       
   120	echo "WITHIN bash -c ... DEMONSTRATE STANDARD OPTION PARSING, lines 120-130:"
   121	while getopts :hv  opt
   122	do 
   123	    case $opt in 
   124	    h)   echo "Option -$opt: Usage: ...    ";;
   125	    v)   echo "Option -$opt: Version: ...  ";;
   126	    [?]) echo "error: while parsing options" 1>&2
   127	         exit 1;;
   128	    esac       
   129	done
   130	shift $(($OPTIND-1))
   131	echo "-----------------------------------------"
       
   132	echo "WITHIN bash -c ... THE FINAL ARGUMENTS, lines 132-135:"
   133	echo \$1: $1
   134	echo \$2: $2
   135	echo \$3: $3
   136	echo "-----------------------------------------"
   137	# done bash -c 
       
   138	                   ' dummy "$@"                    
       
   139	exit $?
       
   140	#
   141	# Obtain html-output:
   142	# $ nl thisfile | enscript --color -Ebash  -whtml -o thisfile.html  

Resultat fra kjøring:

INITIAL COMMAND LINE BEFORE bash -c ... , lines 33-41:

$0: adv-bash-c.bash
$1: -DDEBUG=1
$2: -Dvar1=value1
$3: -Dvar2=A single quote: ' cost $0
$4: -hv
$5: --
$6: arg1
$7: arg2 arg2
$8: arg3
-----------------------------------------
WITHIN bash -c ... -- SHOW EVALUATED ASSIGNMENTS, lines 98, 102-106:
DEBUG='1';
var1='value1';
var2='A single quote: '\'' cost $0';


Show the defined value of 'var1': value1
Show the defined value of 'var2': A single quote: ' cost $0

WITHIN bash -c ...  -- SHOW REMAINING COMMAND LINE, lines 111-117
$1:  -hv
$2:  --
$3:  arg1
$4:  arg2 arg2
$5:  arg3
-----------------------------------------
WITHIN bash -c ... DEMONSTRATE STANDARD OPTION PARSING, lines 120-130:
Option -h: Usage: ...    
Option -v: Version: ...  
-----------------------------------------
WITHIN bash -c ... THE FINAL ARGUMENTS, lines 132-135:
$1: arg1
$2: arg2 arg2
$3: arg3
-----------------------------------------

Referanser:
Nyheter om Linux.
Bash for nybegynnere.
Avansert Bash skripting.
Om regulæruttrykk.
Artikkel i Linux Journal.

Tags:
  • Skriv ut artikkel
  • Abonner med RSS