2

I would like to extract text from file using awk what basicly it works correctly but I would like to make it dymamical using variable for looking for pattern.

HOW IT SHOULD WORKS:

File test_input contains (btw: extract from HP DP omnimm -show_locked_devs)

Type:     Device
Name/Id:  Drive1
Pid:      28405
Host:     Host1

Type:     Cartridge
Name/Id:  Lib1
Pid:      28405
Host:     Host1
Location: 47
...
  • get "Pid" number for Drive1 => command find pattern (Drive1) and display next line from file test_input (28405)

cat test_input | awk 'c&&!--c;/Drive1/{c=1}'| awk '{print $2}' 28405

  • get "Location" number => find all "Pid" numberes and display next 2 line(record) for each match then use grep for filter "Location" from output and display 2nd field (47)

cat test_input | awk 'c&&!--c;/28405/{c=2; print $0}'| grep Location | awk '{print $2}' 47

I have noticed that double quotes in AWK can handle SHELL variables but when I use SAME command in script then I have got error message "awk: The statement cannot be correctly parsed."

DRIVE=Drive1;cat test_input | awk "c&&!--c;/$DRIVE/{c=1}" | awk '{print $2}' 28405

If you have some hints how get work variables from SHELL please let me know. also I know that my commands and redirections are probably complicated but yeah I am not script master :)

FriZY
  • 31
  • 4
  • 3
    Pls. do not use `cat file|awk ...`, should be `awk '' file`. And also use of several `awk` and `grep` in a pipe usually can be reduced into a single `awk`. – TrueY Nov 06 '14 at 09:37
  • Seems to be this: [How to use shell variables in awk script](http://stackoverflow.com/q/19075671/1983854) – fedorqui Nov 06 '14 at 09:39
  • Do this instead `cat <<< "$(more file)" > STDOUT 1>&2 | awk` –  Nov 06 '14 at 09:41
  • It is not clear what you need. Do You need to get the location of `Drive1` and this pattern should be the argument. Or You need two scripts. One is to get a PID for a given pattern (`Drive1`) and the other to get the location based on this pid? – TrueY Nov 06 '14 at 09:44
  • 1
    @Jidder: At least a smiley is missing from your suggestion! ;) – TrueY Nov 06 '14 at 09:46
  • 2
    Only double quote the variable, keep everything else single quoted. like this: `awk 'c&&!--c;/'"$DRIVE"'/{c=1}'` – Robin Hsu Nov 06 '14 at 09:51
  • 3
    There are about 1e3 questions here on SO about passing variables to awk. Please do a search... – Fredrik Pihl Nov 06 '14 at 10:08
  • @TrueY: this is only part of 1 script where I need get location for drive. No arguments are needed as this code will be part of script. – FriZY Nov 06 '14 at 10:22
  • @FriZY: Ok! So if I'm right, you need to parse the input file, looking for the string `Type: Device`. In this block get the `Pid:` value (). (I assume the blocks are separated by an empty line). Then any block should be found which has `Location:` item and its `Pid:` item equals to the PID found in the `Device` block. The output is the " " form. Is it? – TrueY Nov 06 '14 at 11:02
  • @TrueY: yes, you get it! .. but for me input is dynamic pattern $DRIVE and output is $LOCATION. I used var $PID only because I dont know how to do this extraction with only one step. I would like to prefer use echo "$VARIABLE" instead of cat file to AWK. cat test_file is only for debug purposes (I have assumed that it is not matter) – FriZY Nov 06 '14 at 12:44
  • @FriZY: And what if there are multiple such Devices? At the end of my answer all device-location pairs are printed. You can put it into a [tag:bash] hash, if You want... – TrueY Nov 06 '14 at 12:49
  • @RobinHsu - No! Absolutely do not expand the values of shell variables such that they become part of the actual awk script text or you will incur horrendous side effects and, if you're lucky, unfathomable error messages based on the contents of the shell variables. See the FAQ (http://cfajohnson.com/shell/cus-faq-2.html#Q24) for the right way to do it. – Ed Morton Nov 06 '14 at 13:03
  • @TrueY: I have a plan use for output only for 1 drive but output from all devices is maybe better idea. THANK YOU! You are really MASTER! – FriZY Nov 06 '14 at 13:16
  • @FriZY If you update your question to simply show the final expected output, we could help you write a script to do that. – Ed Morton Nov 06 '14 at 13:35

2 Answers2

2

If You need just use environment variables then you can use the ENVIRON built-in hash. If You want to pass arguments to , you can use the -v option.

An example for both:

cat >inputfile <<EOT
aaa
bbbxxx
xxxccc
ddd
EOT

VAR=xxx
awk -vVAR="$VAR" '$0~VAR {print ENVIRON["USER"]":"$0}' inputfile

I added the creation of the sample inputfile.

As I know in some version a white space is needed between -v and VAR.

If I may suggest you would use ' instead of " around the whole script line. It makes the life a bit easier if you use a lot of .

Output:

myuser:bbbxxx
myuser:xxxccc

If I understood well, You need to collect the names of all devices and all locations in non "Device" blocks. I assume clock starting with the tag Type and the tag order is always the same. If not, pls. inform me. Based on these assumptions my code looks like:

awk '$1=="Type:"{dev=$2=="Device"}
dev && $1=="Name/Id:"{name=$2}
dev && $1=="Pid:"{pids[name]=$2}
!dev && $1=="Pid:"{pid=$2}
!dev && $1=="Location:"{locs[pid]=$2}
END {
    for(i in pids) {
        pid = pids[i];
        print i"\t"(pid in locs ? locs[pid] : "None");
    }
}
' inputfile

It fills up the pids and and locs hash, then it prints all device names found in pids hash and the location belongs to this pid (if found).

Output:

Drive1  47

Of course if the location is always after the device block, the line could be printed immediately when the location found. So the END part could be dropped.

TrueY
  • 7,360
  • 1
  • 41
  • 46
0

It's not clear what you want but maybe this:

$ cat tst.awk
BEGIN{ RS=""; FS="[:[:space:]]+" }
{
    for (i=1;i<=NF;i+=2)
       name2val[$i] = $(i+1)
}   
(name2val[key] == val) && (tgt in name2val) {
    print name2val[tgt]
}
$
$ awk -v key="Name/Id" -v val="Drive1" -v tgt="Pid" -f tst.awk file
28405
$
$ awk -v key="Pid" -v val="28405" -v tgt="Location" -f tst.awk file
47
Ed Morton
  • 188,023
  • 17
  • 78
  • 185