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:-