Shell scripting notes

From raju

piping into a braced block

The idea here is iterate over a bunch of items by sending them as input into a while block.

    $svn log -q foo.py --limit 10 | grep -E -e "r[[:digit:]]+" -o | {
    >   while read r
    >   do
    >     echo $r
    >   done
    > }
    r377
    r362
    r360
    r350
    r334
    r330
    r275
    r244
    

Ref:- https://stackoverflow.com/a/283168/6305733

read each line of a file in a for loop

Approach 1:

    $while read r
    do
      echo $r
    done < /path/to/file
    

Approach 2:

    $cat /path/to/file |
    while read r
    do
      echo $r
    done
    

Check if a word exists in an array

Consider

    $ DAYS=(sun mon tue wed thu fri sat)
    

Create an auxiliary variable with the string to be matched as a regex. So to match the word "tue", do

    $ TODAY="\btue\b"
    $ [[ ${DAYS[@]} =~ $TODAY ]] && echo 1 || echo 0
    1
    

where '\b' denotes the word boundary.

You can check that '\b' really works. For example

    $ TODAY="tu"
    $ [[ ${DAYS[@]} =~ $TODAY ]] && echo 1 || echo 0
    1
    
    $ TODAY="\btu\b"
    $ [[ ${DAYS[@]} =~ $TODAY ]] && echo 1 || echo 0
    0
    

shows that there is a string "tu" in DAYS but not the word "tu". Similarly

    $ TODAY="n m"
    $ [[ ${DAYS[@]} =~ $TODAY ]] && echo 1 || echo 0
    1
    
    $ TODAY="\bn m\b"
    $ [[ ${DAYS[@]} =~ $TODAY ]] && echo 1 || echo 0
    0
    

shows that there is a string "n m" some where in DAYS but not as a word.

Alternatively, you can use command line substitution instead of creating an extra variable

    $ [[ ${DAYS[@]} =~ $(echo "\btue\b") ]] && echo 1 || echo 0
    1
    $ [[ ${DAYS[@]} =~ $(echo "\btu\b") ]] && echo 1 || echo 0
    0
    $ [[ ${DAYS[@]} =~ $(echo "\bmon\b") ]] && echo 1 || echo 0
    1
    $ [[ ${DAYS[@]} =~ $(echo "\bn m\b") ]] && echo 1 || echo 0
    0
    

However, do not use string literals for this. It can match simple strings but not the word boundaries.

    $ [[ ${DAYS[@]} =~ "\btue\b" ]] && echo 1 || echo 0
    0
    $ [[ ${DAYS[@]} =~ "tue" ]] && echo 1 || echo 0
    1
    $ [[ ${DAYS[@]} =~ "n m" ]] && echo 1 || echo 0
    1
    

Tested on Debian Stretch (9.3) with

    $ bash --version
    GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
    Copyright (C) 2016 Free Software Foundation, Inc.
    

Ref:-

tags | checking for word boundaries, regex, "=~" operator, equal tilde operator in shell scripts

Difference between * and @ in bash arrays

Consider

    $ DAYS=(sun mon tue wed thu fri sat)
    

If you do ${DAYS[@]}, ${DAYS[*]} then there is no difference.

    $ for i in ${DAYS[@]}; do echo $i; done
    sun
    mon
    tue
    wed
    thu
    fri
    sat
    
    $ for i in ${DAYS[*]}; do echo $i; done
    sun
    mon
    tue
    wed
    thu
    fri
    sat
    

But it makes a difference if they are enclosed in quotes. For example, "${DAYS[@]}" expands to seven words as "sun" "mon" "tue" "wed" "thu" "fri" "sat. But "${DAYS[*]}" expands to a single word "sun mon tue wed thu fri sat"

    $ for i in "${DAYS[@]}"; do echo $i; done
    sun
    mon
    tue
    wed
    thu
    fri
    sat
    
    $ for i in "${DAYS[*]}"; do echo $i; done
    sun mon tue wed thu fri sat
    

Ref:- https://stackoverflow.com/questions/3060447/bash-arrays-what-is-difference-between-array-name-and-array-name

Interactively test a condition in a shell

This will return 1 if foo.txt exists, 0 otherwise.

    [[ -f foo.txt ]] && echo 1 || echo 0
    

To see how it works

    [[ "SUNDAY" == "SUNDAY" ]] && echo 1 || echo 0
    1
    
    [[ "SUNDAY" == "Sunday" ]] && echo 1 || echo 0
    0
    
    [[ "SUNDAY" == "SUN" ]] && echo 1 || echo 0
    0
    
    [[ "SUNDAY" == "MONDAY" ]] && echo 1 || echo 0
    0
    

Call perl script from shell

Create the shell script as follows. It is basically a pass through for the perl function.

=> cat hello
#! /bin/tcsh -f

~/work/perl/from_shell_script/hello.pl $*

Now create a simple perl script that dumps its arguments.

=> cat hello.pl
#! /usr/bin/perl -w

use strict;
use warnings;
use Data::Dumper qw(Dumper);

print Dumper \@ARGV;

Run the perl program

=> ./hello.pl -a -b raju
$VAR1 = [
          '-a',
          '-b',
          'raju'
        ];

Same as above except that we use the shell wrapper.

=> ./hello -a -b raju
$VAR1 = [
          '-a',
          '-b',
          'raju'
        ];

Shell expands the arguments before passing it to the calling scripts.

=> ls
hello*  hello.pl*

=> ./hello hello*
$VAR1 = [
          'hello',
          'hello.pl'
        ];

=> ./hello "hello*"
$VAR1 = [
          'hello',
          'hello.pl'
        ];

=> ./hello 'hello*'
$VAR1 = [
          'hello',
          'hello.pl'
        ];

Specify a default if not initialized on command line

     % cat foo.sh 
    #! /bin/bash
    output=${1:-kama raju `date +'%Y%m%d_%H%M%S'`}
    echo $output
    
     % chmod +x foo.sh
    
     % foo.sh          
    kama raju 20180316_101112
    
     % foo.sh foo
    foo
    
     % foo.sh foo bar
    foo
    
     % foo.sh "foo bar"
    foo bar
    

Ref:-

useful links