-1

Writing a shell script that receives 3 arguments but within the script one of the commands needs to check the first value after a delimiter is applied

#!/bin/bash
awk -F'\t' '$1 ~ /$1/&&/$2/ {print $1FS$3}' $3

this command is called:

bash search.sh 5 AM filename.txt

And should execute as follows:

awk -F'\t' '$1 ~ /5/&&/AM/ {print $1FS$3}' filename.txt

The command functions properly outside of the shell script, returns nothing right now when using it inside the shell script. filename.txt :

03:00:00 AM John-James Hayward  Evalyn Howell   Chyna Mercado
04:00:00 AM Chyna Mercado   Cleveland Hanna Katey Bean
05:00:00 AM Katey Bean  Billy Jones Evalyn Howell
06:00:00 AM Evalyn Howell   Saima Mcdermott Cleveland Hanna
07:00:00 AM Cleveland Hanna Abigale Rich    Billy Jones

Expected output:

05:00:00 AM Billy Jones
Cyrus
  • 84,225
  • 14
  • 89
  • 153
  • Is there a tab after `AM`? If not, the time and name will be one field. – dan Nov 21 '21 at 08:24
  • See: [Difference between single and double quotes in bash](http://stackoverflow.com/q/6697753/3776858) – Cyrus Nov 21 '21 at 09:20
  • Can your timestamps every be anything other than on the hour, e.g. could you have `03:05:20` in your input? Are you trying to print lines that occur at exactly that time or within the hour that starts with that first number? – Ed Morton Nov 21 '21 at 19:38
  • How is midnight represented in your input - `00:00:00 AM` or something else? – Ed Morton Nov 21 '21 at 19:59
  • There is a tab after AM. The timestamps could theoretically contain more than just the hour, for the purposes of this assignment they would not. And yes, midnight is represented as 12:00:00 AM – Rob_Coder_1123 Nov 22 '21 at 23:58

3 Answers3

0

Your arguments are not being expanded by the shell when you single quote them. You can use awk variables (as suggested by @JohnKugelman ) to more clearly separate shell and awk code:

#!/bin/bash
awk -F'\t' -v p="$1" -v p2="$2" '($0 ~ p && $0 ~ p2) {print $1FS$3}' "$3"

I use generic variable names here (p and p2) to emphasize that you are not anchoring your regex so they really do match on the hole line instead of hour and am/pm as intended.

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • 1
    It would be better to pass variables to awk with `-v` to avoid trouble with special characters. – John Kugelman Nov 21 '21 at 07:08
  • 1
    @EdMorton you, sir, are a tease :-) – Allan Wind Nov 22 '21 at 03:43
  • So -v p="$1" creates a temporary variable within the command, referenced using p and representing $1 as supplied to the shell script? Meaning it would have to be included again if I were to have another line in the code that needed to reference it? Also, $0 ~ p is checking the whole delimited section for p , correct? – Rob_Coder_1123 Nov 23 '21 at 00:08
  • Yes, it creates an awk variable called `p` with the value "$1" which the shell expands to the the first command line argument in this case. You can reference `p` in your awk script as many times as you want. Yes, the awk variable `$0` is the whole line, and `$0 ~ p` checks it against the regex `p`. You set the field separator with -F, so you can use $1, $2 etc (it's not clear what is space and what is tab separated in your test data, or if you really want to match against specific fields; see the other answers for that). – Allan Wind Nov 23 '21 at 02:36
  • Btw, you can also skip the shell entirely and just write an awk scripting using the she bang `#!/usr/bin/awk -f` but that might be gnu awk specific. See https://stackoverflow.com/questions/1418245/invoking-a-script-which-has-an-awk-shebang-with-parameters-vars – Allan Wind Nov 23 '21 at 02:43
0

Don't embed shell variables in an awk script.

Here's a solution with some explanatory comments:

#!/bin/bash

[[ $# -lt 2 ]] && exit 1       ## two args required, plus files or piped/redirected input

hour="$(printf '%02d' "$1")"   ## add a leading zero if neccesary
pm=${2^^}                      ## capitalise

shift 2

time="^$hour:.* $pm\$"         ## match the whole time field

awk -F '\t' -v time="$time" \
'$1 ~ time {print $1,$3}' "$@" ## if it matches, print fields 1 and 3 (date, second name)

Usage is bash search.bash HOUR PM [FILE] ..., or ./search HOUR PM [FILE] ... if you make it executable. For example ./search 5 am file.txt or ./search 05 AM file.txt.

I'm assuming that every field is delimited by tabs.

dan
  • 4,846
  • 6
  • 15
  • If every field is delimited by tabs then `time="^$hour:.* $pm\$"` with a blank instead of a tab between the first 2 fields can never match any input line. The `.*` is a bit fragile too in case `AM` or `PM` can occur mid-word further along the input line (bear in mind some names exist that may not conform to your expectations of which characters are used in what orders!). – Ed Morton Nov 21 '21 at 19:49
0

This is probably what you're trying to do (untested, using any awk):

#!/usr/bin/env bash

awk -v hour="$1" -v ampm="$2" '
    BEGIN {
        FS = OFS = "\t"
        time = sprintf("%02d:00:00", hour)
    }
    ($1 == time) && ($2 == ampm) {
        print $1, $3
    }
' "${3:--}"

Note that the above would work even if your input file contained 10:00:00 AM and the arguments used were 1 AM. Some of the other solutions would fail given that as they're using a partial regexp comparison and so the arg 1 would match the 1s in input of 10:00:00, 11:00:00, or 12:00:00.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185