awk - piping string to external gfortran-built executable in unix - bash

I have the following awk-script "create_grid.awk" which pipes a command through to an external exe (iri_win.exe/iri_unix.exe built with gfortran) and reads back the response via getline (creating a grid of values with two nested for-loops). Whereas everything works like a charm in windows the matter in the unix environment goes wrong.
!/bin/awk -f
BEGIN {
is_windows = 0;
if (index(tolower(ENVIRON["OS"]), "windows") > 0) {
is_windows = 1;
}
exit
}
{
}
END {
mystring=""
#myvar=""
#CMD = "cmdline | getline myvar"
#print "" > "iri_fortran_output_grid.txt"
for (j = -9 ; j <= -9; j+=2){
for (i = -2; i <= 2; i++){
pipe= "\"" j "," i ",0.300\n2017,823,0,12.750\n20200" "\""
#pipe= "\"49,12,0.300\n2017,823,1,12.750\n20200 \""
#pipe= "\"49,12,0.300\n2017,823,1,12.750\n20200\""
if (is_windows)
cmdline = "echo -e " pipe " | ./iri_win.exe"
else
cmdline = "echo -e " pipe " | ./iri_unix.exe"
print cmdline
if ( (cmdline | getline myvar) > 0 ) {
#close(cmdline | getline myvar)
print "latitude " j " , longitude " i " done, TEC: " myvar " ;"
}
else{
#close(cmdline | getline myvar)
print "error in latitude " j " , longitude " i
}
close(cmdline)
#close(getline)
#cmdline | getline myvar
#myvar = $0
mystring = mystring myvar " "
}
# print one line into result file
sub(/[ \t]+$/, "", mystring)
sub(/^[ \t]+/, "", mystring)
print mystring > "iri_fortran_output_grid.txt"
print "longitude " j " done"
mystring=""
fflush("iri_fortran_output_grid.txt")
fflush(stdout)
#close("iri_fortran_output_grid.txt")
}
}
Output of awk script in Unix OS:
loren32#nautilus:~/iri_exe_analysis$gawk -f ./create_grid.awk
At line 107 of file iri_4_tec_al_2.for (unit = 5, file = 'stdin')
Fortran runtime error: Bad real number in item 1 of list input
error in latitude -9 , longitude 0
echo -e "-9,1,0.300
2017,823,0,12.750
20200" | ./iri_unix.exe
...
Output in Unix OS only executing external exe :
loren32#nautilus:~/iri_exe_analysis$ echo -e "-9,1,0.300\n2017,823,1,12.750\n20200" | ./iri_unix.exe
18.452
As can be seen when I pipe my string via echo -e "stringcontent" to iri_unix.exe from bash in Unix it works but the call from within the awk script fails.
I suspect the quotes work differently in unix and somehow additional disturbing string data is sent to iri_unix.exe - hence the error message "Bad real number in item 1 of list input".
I wonder what is going wrong and how to correct my awk script to make it work in Unix OS. In Windows the script works fine

The workaround is to write
echo "..."
in the Unix Environment and leave out the -e option. That die the trick for me.

Related

Is there a Unix utility to prepend to stdin the time in milliseconds since the start of the command?

I ended up writing a quick little script for this in NodeJS, but I was wondering if there was a utility you could feed text into which would prepend each line with some text -- in my specific case, the time elapsed in milliseconds since the start of the programm. Ideally, the use would be something like:
longrunningprocess | prepend-timestamp
This could help do some fast debugging.
This question looks very similar to Is there a Unix utility to prepend timestamps to stdin?, but is not the same, I would like to prepend milliseconds since the start of the program, not the current timestamp.
Here is my version using NodeJS:
#!/bin/env node
var moment = require("moment");
process.stdin.resume()
var fullData = "";
var t = +new Date();
process.stdin.on('data', function(data) {
fullData += data;
splitted = fullData.split("\n");
splitted.forEach(function (part) {
if (part === "" ) {
return process.stdout.write("\n");
}
process.stdout.write( (+new Date() - t) + " " + part + "\n");
})
fullData = splitted[splitted.length-1];
});
process.stdout.on('error', function(err) {
if (err.code === 'EPIPE') return process.exit()
process.emit('error', err)
});
Bonus points if the ANSI escapes are kept (eg to see the colors of the initial command),
It would be even better if the precision (eg number of decimals) could be configurable.
If you are on Linux, the ts command fits the bill.
If you only need resolution to the second, you can just use bash:
start=$SECONDS
longrunningprocess | while IFS= read -r line; do
echo "$((SECONDS - start)): $line"
done
The $SECONDS variable gives you the number of seconds since the shell started. It's very useful for this kind of duration calculation.
OK, I didn't fully understand all your requirements. Here's a version with Perl:
atime () {
local precision="${1:-5}"
perl -MTime::HiRes=time -sne '
BEGIN {$start = time}
printf "%.${prec}f %s", (time - $start), $_
' -- -prec="$precision"
}
longrunningprocess | atime 3
Demo:
$ { echo foo; sleep 1; echo bar; } | atime
0.00010 foo
0.98499 bar
$ { echo foo; sleep 1; echo bar; } | atime 3
0.000 foo
0.980 bar
I ended up using both ts to print the current timestamp with milliseconds and awk to calculate the difference:
#!/bin/sh
# atime <precision>
precision=${1:-"5"}
ts '%.s' |
awk -v p="$precision" 'NR == 1 {st = $1} {diff = $1 - st; sub(/^[[:digit:].]+ /,""); printf "%."p"f %s\n", diff, $0}'
For example
{ echo "foo" ; sleep 1 ; echo "bar" ; } | atime
Prints :
0.00000 foo
0.97711 bar

Parsing IBM MQ Series Runmqsc command output

I'm using sed/awk to parse mq runmqsc output. We want to get certain fields displayed on a single line. This seems to be a straightforward sed/awk problem.
echo "display conn(*) ALL" |
runmqsc <BrokerName> |
awk '{ RS = "AMQ8276: Display Connection details." } ; { print $0 }' |
sed -e 's/( )/()/g'
5724-H72 (C) Copyright IBM Corp. 1994, 2009. ALL RIGHTS RESERVED.
Starting MQSC for queue manager .
1 : display conn(*) ALL
CONN(3923A95601000020)
EXTCONN(414D51435465737442726F6B65725553)
TYPE(CONN)
PID(9263) TID(1)
APPLDESC(WebSphere MQ Object Authority Manager)
APPLTAG(amqzfuma) APPLTYPE(SYSTEM)
ASTATE(NONE) CHANNEL()
CONNAME() CONNOPTS(MQCNO_FASTPATH_BINDING)
USERID(mqm) UOWLOG()
UOWSTDA() UOWSTTI()
UOWLOGDA() UOWLOGTI()
URTYPE(QMGR)
EXTURID(XA_FORMATID[00000000] XA_GTRID[] XA_BQUAL[])
QMURID(0.0) UOWSTATE(NONE)
This code:
echo "display conn(*) ALL" | runmqsc TestBrokerUS | awk '{ RS = "AMQ8276: Display Connection details." } ; { print $0 }' | sed -e 's/( )/()/g' |
sed -n -e 's/^.* CHANNEL(\(.*\).*) /\1/p' -e 's/^.* USERID(\(.*\).*)/\1/p' -e 's/^.* CONNOPTS(\(.*\).*)/\1/p' -e 's/^.* CONN(\(.*\).*)/\1/p' -e 's/^.* CONNAME(\(.*\).*)/\1/p'
Returns this:
3923A95601000020)
) CONNOPTS(MQCNO_FASTPATH_BINDING)
Need to parse out these fields on 1 line. Thoughts?
One option would be to replace the newlines from the output and then add one newline per each dis conn.
Example:
echo "dis conn(*) all" | runmqsc MQ8QMGR|sed ':a;N;$!ba;s/\n/ /g'|sed 's/UOWSTATE/\n/g'
Other option would be to store the values in variables and then print when you get the last attribute. Here is an example to parse dis qs output.
/QUEUE/{i1=index($1,"("); i2=index($1,")")-1; QUEUE=substr($1,i1+1,i2-i1);}
/CURDEPTH/{i1=index($1,"("); i2=index($1,")")-1; CURDEPTH=substr($1,i1+1,i2-i1);
i3=index($2,"("); i4=index($2,")")-1; IPPROCS=substr($2,i3+1,i4-i3);
}
/LGETDATE/{i1=index($1,"("); i2=index($1,")")-1; LGETDATE=substr($1,i1+1,i2-i1);
i3=index($2,"("); i4=index($2,")")-1; LGETTIME=substr($2,i3+1,i4-i3);
}
/LPUTDATE/{i1=index($1,"("); i2=index($1,")")-1; LPUTDATE=substr($1,i1+1,i2-i1);
i3=index($2,"("); i4=index($2,")")-1; LPUTTIME=substr($2,i3+1,i4-i3)
}
/MSGAGE/{i3=index($2,"("); i4=index($2,")")-1; MSGAGE=substr($2,i3+1,i4-i3);}
/OPPROCS/{i1=index($1,"("); i2=index($1,")")-1; OPPROCS=substr($1,i1+1,i2-i1);
i3=index($2,"("); i4=index($2,",")-1; QTIME=substr($2,i3+1,i4-i3);
i5=1; i6=index($3,")")-1; QTIME2=substr($3,i5,i6-i5+1);
}
/UNCOM/{
i1=index($1,"("); i2=index($1,")")-1; UNCOM=substr($1,i1+1,i2-i1);
if( QUEUE == "SYSTEM.CLUSTER.TRANSMIT.QUEUE" )
{
print date "|" QUEUE "|" CURDEPTH "|" LGETDATE "|" LGETTIME "|" LPUTDATE "|" LPUTTIME "|" MSGAGE "|" QTIME "," QTIME2 "|" UNCOM;
}
}
I use the following script:
#! /usr/bin/env gawk -f
#
# Parses output of 'runmqsc' command and prints a comma separated list of the
# attributes on one line per object.
#
# CAVEAT: Fails if '(' or ')' characters are in attribute values (e.g. DESC)
#
# Daniel Steinmann, Mai 2015
function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s }
function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s }
function trim(s) { return rtrim(ltrim(s)); }
BEGIN {
RS = "AMQ[0-9]+: [^.]*."
FS = "[()]"
}
{
# Filter out header
if (NR == 1) next
for (i = 1; i <= NF; i = i+2) {
key = trim($i)
value = trim($(i+1))
# Filter out empty lines and trailer
if (key ~ /[ ]/ || key == "") continue
if (i > 1) printf(", ")
printf("%s(%s)", key, value)
}
printf("\n")
}
It produces output like this:
$ echo "display conn(*) all | runmqsc MYQMGR | ./parse_runmqsc_output.gawk
CONN(F290EE5720001801), EXTCONN(414D51435858582E534232492E4D4149), ...
CONN(F290EE572009F312), EXTCONN(414D51435858582E534232492E4D4149), ...
You can easily adapt the gawk script to produce output the way you want.

Using bc in awk

I am trying to use bc in an awk script. In the code below, I am trying to convert hexadecimal number to binary and store it in a variable.
#!/bin/awk -f
{
binary_vector = $(bc <<< "ibase=16;obase=2;FF")
}
Where do I go wrong?
Not saying it's a good idea but:
$ awk 'BEGIN {
cmd = "bc <<< \"ibase=16;obase=2;FF\""
rslt = ((cmd | getline line) > 0 ? line : -1)
close(cmd)
print rslt
}'
11111111
Also see http://gnu.org/software/gawk/manual/gawk.html#Bitwise-Functions and http://gnu.org/software/gawk/manual/gawk.html#Nondecimal-Data
The following one-liner Awk script should do what you want:
awk -vVAR=$(read -p "Enter number: " -u 0 num; echo $num) \
'BEGIN{system("echo \"ibase=16;obase=2;"VAR"\"|bc");}'
Explanation:
-vVAR Passes the variable VAR into Awk
-vVAR=$(read -p ... ) Sets the variable VAR from the
shell to the user input.
system("echo ... |bc") Uses the Awk system built in command to execute the shell commands. Notice how the quoting stops at the variable VAR and then continues just after it, thats so that Awk interprets VAR as an Awk variable and not as part of the string put into the system call.
Update - to use it in an Awk variable:
awk -vVAR=$(read -p "Enter number: " -u 0 num; echo $num) \
'BEGIN{s="echo \"ibase=16;obase=2;"VAR"\"|bc"; s | getline awk_var;\
close(s); print awk_var}'
s | getline awk_var will put the output of the command s into the Awk variable awk_var. Note the string is built before sending it to getline - if not (unless you parenthesize the string concatenation) Awk will try to send it to getline in separate pieces %s VAR %s.
The close(s) closes the pipe - although for bc it doesn't matter and Awk automatically closes pipes upon exit - if you put this into a more elaborate Awk script it is best to explicitly close the pipe. According to the Awk documentation some commands such as mail will wait on the pipe to close prior to completion.
http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_39.html
By the way you wrote your example, it looks like you want to convert an awk record ( line ) into an associative array. Here's an awk executable script that allows that by running the bc command over values in a split type array:
#!/usr/bin/awk -f
{
# initialize the a array
cnt = split($0, a, FS)
if( convertArrayBase(10, 2, a, cnt) > -1 ) {
# use the array here
for(i=1; i<=cnt; i++) {
print a[i]
}
}
}
# Destructively updates input array, converting numbers from ibase to obase
#
# #ibase: ibase value for bc
# #obase: obase value for bc
# #a: a split() type associative array where keys are numeric
# #cnt: size of a ( number of fields )
#
# #return: -1 if there's a getline error, else cnt
#
function convertArrayBase(ibase, obase, a, cnt, i, b, cmd) {
cmd = sprintf("echo \"ibase=%d;obase=%d", ibase, obase)
for(i=1; i<=cnt; i++ ) {
cmd = cmd ";" a[i]
}
cmd = cmd "\" | bc"
i = 0 # reset i
while( (cmd | getline b) > 0 ) {
a[++i] = b
}
close( cmd )
return i==cnt ? cnt : -1
}
When used with an input of:
1 2 3
4 s 1234567
this script outputs the following:
1
10
11
100
0
100101101011010000111
The convertArrayBase function operates on split type arrays. So you have to initialize the input array (a here) with the full row (as shown) or a field's subflds(not shown) before calling the it. It destructively updates the array.
You could instead call bc directly with some helper files to get similar output. I didn't find that bc supported - ( stdin as a file name ) so
it's a little more than I'd like.
Making a start_cmds file like this:
ibase=10;obase=2;
and a quit_cmd like:
;quit
Given an input file (called data.semi) where the data is separated by a ;, like this:
1;2;3
4;s;1234567
you can run bc like:
$ bc -q start_cmds data.semi quit_cmd
1
10
11
100
0
100101101011010000111
which is the same data that the awk script is outputting, but only calling bc a single time with all of the inputs. Now, while that data isn't in an awk associative array in a script, the bc output could be written as stdin input to awk and reassembed into an array like:
bc -q start_cmds data.semi quit_cmd | awk 'FNR==NR {a[FNR]=$1; next} END { for( k in a ) print k, a[k] }' -
1 1
2 10
3 11
4 100
5 0
6 100101101011010000111
where the final dash is telling awk to treat stdin as an input file and lets you add other files later for processing.

Parsing iw wlan0 scan output

I wrote wlan manager script to handle open/ad-hoc/wep/wpa2 networks. Now im trying to parse iw wlan0 scan output to get nice scan feature to my script. My goal is to get output like this :
SSID channel signal encryption
wlan-ap 6 70% wpa2-psk
test 1 55% wep
What i have achived already is output like this :
$ iw wlan0 scan | grep 'SSID\|freq\|signal\|capability' | tac
SSID: Koti783
signal: -82.00 dBm
capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime (0x0531)
freq: 2437
I have been trying to study bash/sed/awk but havent found yet a way to achieve what im trying. So what is good way to achieve that?
Here is my final solution based of Sudo_O answer:
$1 == "BSS" {
MAC = $2
wifi[MAC]["enc"] = "Open"
}
$1 == "SSID:" {
wifi[MAC]["SSID"] = $2
}
$1 == "freq:" {
wifi[MAC]["freq"] = $NF
}
$1 == "signal:" {
wifi[MAC]["sig"] = $2 " " $3
}
$1 == "WPA:" {
wifi[MAC]["enc"] = "WPA"
}
$1 == "WEP:" {
wifi[MAC]["enc"] = "WEP"
}
END {
printf "%s\t\t%s\t%s\t\t%s\n","SSID","Frequency","Signal","Encryption"
for (w in wifi) {
printf "%s\t\t%s\t\t%s\t%s\n",wifi[w]["SSID"],wifi[w]["freq"],wifi[w]["sig"],wifi[w]["enc"]
}
}'
Output:
$ sudo iw wlan0 scan | awk -f scan.awk
SSID Frequency Signal Encryption
netti 2437 -31.00 dBm Open
Koti783 2437 -84.00 dBm WPA
WLAN-AP 2462 -85.00 dBm WPA
it's generally bad practice to try parsing complex output of programs intended for humans to read (rather than machines to parse).
e.g. the output of iw might change depending on the language settings of the system and/or the version of iw, leaving you with a "manager" that only works on your development machine.
instead you might use the same interface that iw uses to get it's information: the library backend libnl
you might also want to have a look at the wireless-tools (iwconfig, iwlist,...) that use the libiw library.
Here is an GNU awk script to get you going that grabs the SSIDs and the channel for each unique BSS:
/^BSS / {
MAC = $2
}
/SSID/ {
wifi[MAC]["SSID"] = $2
}
/primary channel/ {
wifi[MAC]["channel"] = $NF
}
# Insert new block here
END {
printf "%s\t\t%s\n","SSID","channel"
for (w in wifi) {
printf "%s\t\t%s\n",wifi[w]["SSID"],wifi[w]["channel"]
}
}
It should be easy for you to add the new blocks for signal and encryption considering all the studying you have been doing.
Save the script to file such as wifi.awk and run like:
$ sudo iw wlan0 scan | awk -f wifi.awk
The output will be in the formatted requested:
SSID channel
wlan-ap 6
test 1
Here is a simple Bash function which uses exclusively Bash internals and spawns only one sub-shell:
#!/bin/bash
function iwScan() {
# disable globbing to avoid surprises
set -o noglob
# make temporary variables local to our function
local AP S
# read stdin of the function into AP variable
while read -r AP; do
## print lines only containing needed fields
[[ "${AP//'SSID: '*}" == '' ]] && printf '%b' "${AP/'SSID: '}\n"
[[ "${AP//'signal: '*}" == '' ]] && ( S=( ${AP/'signal: '} ); printf '%b' "${S[0]},";)
done
set +o noglob
}
iwScan <<< "$(iw wlan0 scan)"
Output:
-66.00,FRITZ!Box 7312
-56.00,ALICE-WLAN01
-78.00,o2-WLAN93
-78.00,EasyBox-7A2302
-62.00,dlink
-74.00,EasyBox-59DF56
-76.00,BELAYS_Network
-82.00,o2-WLAN20
-82.00,BPPvM
The function can be easily modified to provide additional fields by adding a necessary filter into the while read -r AP while-loop, eg:
[[ "${AP//'last seen: '*}" == '' ]] && ( S=( ${AP/'last seen: '} ); printf '%b' "${S[0]},";)
Output:
-64.00,1000,FRITZ!Box 7312
-54.00,492,ALICE-WLAN01
-76.00,2588,o2-WLAN93
-78.00,652,LN8-Gast
-72.00,2916,WHITE-BOX
-66.00,288,ALICE-WLAN
-78.00,800,EasyBox-59DF56
-80.00,720,EasyBox-7A2302
-84.00,596,ALICE-WLAN08
I am using such solution for openwrt:
wlan_scan.sh
#!/bin/sh
sudo iw dev wlan0 scan | awk -f wlan_scan.awk | sort
wlan_scan.awk
/^BSS/ {
mac = gensub ( /^BSS[[:space:]]*([0-9a-fA-F:]+).*?$/, "\\1", "g", $0 );
}
/^[[:space:]]*signal:/ {
signal = gensub ( /^[[:space:]]*signal:[[:space:]]*(\-?[0-9.]+).*?$/, "\\1", "g", $0 );
}
/^[[:space:]]*SSID:/ {
ssid = gensub ( /^[[:space:]]*SSID:[[:space:]]*([^\n]*).*?$/, "\\1", "g", $0 );
printf ( "%s %s %s\n", signal, mac, ssid );
}
result
-62.00 c8:64:c7:54:d9:05 a
-72.00 70:72:3c:1c:af:17 b
-81.00 78:f5:fd:be:33:cb c
There is a bug in the awk script above.
The following code will not work if the SSID has spaces in the name. The received result will be the first token of the SSID name only.
$1 == "SSID:" {
wifi[MAC]["SSID"] = $2
}
When printing $0, $1, $2:
$0: SSID: DIRECT-82-HP OfficeJet 8700
$1: SSID:
$2: DIRECT-82-HP
One possibly solution is to take a substr of $0 which contains leading spaces, the token "SSID: " and the provided multi-token network name.
Any other suggestions?
I've taken awk code from Ari Malinen and reworked it a bit, because iw output is not stable and changes, also there are other issues like spaces in SSID. I put it on github in case if I'll change it in the future.
#!/usr/bin/env awk -f
$1 ~ /^BSS/ {
if($2 !~ /Load:/) { #< Escape "BBS Load:" line
gsub("(\\(.*|:)", "", $2)
MAC = toupper($2)
wifi[MAC]["enc"] = "OPEN"
wifi[MAC]["WPS"] = "no"
wifi[MAC]["wpa1"] = ""
wifi[MAC]["wpa2"] = ""
wifi[MAC]["wep"] = ""
}
}
$1 == "SSID:" {
# Workaround spaces in SSID
FS=":" #< Changing field separator on ":", it should be
# forbidded sign for SSID name
$0=$0
sub(" ", "", $2) #< remove first whitespace
wifi[MAC]["SSID"] = $2
FS=" "
$0=$0
}
$1 == "capability:" {
for(i=2; i<=NF; i++) {
if($i ~ /0x[0-9]{4}/) {
gsub("(\\(|\\))", "", $i)
if (and(strtonum($i), 0x10))
wifi[MAC]["wep"] = "WEP"
}
}
}
$1 == "WPA:" {
wifi[MAC]["wpa1"] = "WPA1"
}
$1 == "RSN:" {
wifi[MAC]["wpa2"] = "WPA2"
}
$1 == "WPS:" {
wifi[MAC]["WPS"] = "yes"
}
$1 == "DS" {
wifi[MAC]["Ch"] = $5
}
$1 == "signal:" {
match($2, /-([0-9]{2})\.00/, m)
wifi[MAC]["Sig"] = m[1]
}
$1 == "TSF:" {
gsub("(\\(|d|,)", "", $4)
match($5, /([0-9]{2}):([0-9]{2}):/, m)
day = $4
hour = m[1]
min = m[2]
wifi[MAC]["TSF"] = day"d"hour"h"min"m"
}
END {
for (w in wifi) {
if (wifi[w]["wep"]) {
if (wifi[w]["wpa1"] || wifi[w]["wpa2"])
wifi[w]["enc"] = wifi[w]["wpa1"]wifi[w]["wpa2"]
else
wifi[w]["enc"] = "WEP"
}
printf "%s:%s:%s:%s:%s:%s:%s\n", w, wifi[w]["SSID"], wifi[w]["enc"], \
wifi[w]["WPS"], wifi[w]["Ch"], wifi[w]["Sig"], wifi[w]["TSF"]
}
}
Output:
A5FEF2C499BB:test-ssid2:OPEN:no:9:43:0d00h00m
039EFACA9A8B:test-ssid2:WPA1:no:9:33:0d00h00m
038BF3C1988B:test-ssid2:WPA2:no:9:35:0d00h00m
028EF3C2997B:test-ssid2:WPA1:no:9:35:0d00h03m
if you wonder what if($2 !~ /Load:/) does, well on some routers there might be "BSS Load:" string.

Extracting multiple parts of a string using bash

I have a caret delimited (key=value) input and would like to extract multiple tokens of interest from it.
For example: Given the following input
$ echo -e "1=A00^35=D^150=1^33=1\n1=B000^35=D^150=2^33=2"
1=A00^35=D^22=101^150=1^33=1
1=B000^35=D^22=101^150=2^33=2
I would like the following output
35=D^150=1^
35=D^150=2^
I have tried the following
$ echo -e "1=A00^35=D^150=1^33=1\n1=B000^35=D^150=2^33=2"|egrep -o "35=[^/^]*\^|150=[^/^]*\^"
35=D^
150=1^
35=D^
150=2^
My problem is that egrep returns each match on a separate line. Is it possible to get one line of output for one line of input? Please note that due to the constraints of the larger script, I cannot simply do a blind replace of all the \n characters in the output.
Thank you for any suggestions.This script is for bash 3.2.25. Any egrep alternatives are welcome. Please note that the tokens of interest (35 and 150) may change and I am already generating the egrep pattern in the script. Hence a one liner (if possible) would be great
You have two options. Option 1 is to change the "white space character" and use set --:
OFS=$IFS
IFS="^ "
set -- 1=A00^35=D^150=1^33=1 # No quotes here!!
IFS="$OFS"
Now you have your values in $1, $2, etc.
Or you can use an array:
tmp=$(echo "1=A00^35=D^150=1^33=1" | sed -e 's:\([0-9]\+\)=: [\1]=:g' -e 's:\^ : :g')
eval value=($tmp)
echo "35=${value[35]}^150=${value[150]}"
To get rid of the newline, you can just echo it again:
$ echo $(echo "1=A00^35=D^150=1^33=1"|egrep -o "35=[^/^]*\^|150=[^/^]*\^")
35=D^ 150=1^
If that's not satisfactory (I think it may give you one line for the whole input file), you can use awk:
pax> echo '
1=A00^35=D^150=1^33=1
1=a00^35=d^157=11^33=11
' | awk -vLIST=35,150 -F^ ' {
sep = "";
split (LIST, srch, ",");
for (i = 1; i <= NF; i++) {
for (idx in srch) {
split ($i, arr, "=");
if (arr[1] == srch[idx]) {
printf sep "" arr[1] "=" arr[2];
sep = "^";
}
}
}
if (sep != "") {
print sep;
}
}'
35=D^150=1^
35=d^
pax> echo '
1=A00^35=D^150=1^33=1
1=a00^35=d^157=11^33=11
' | awk -vLIST=1,33 -F^ ' {
sep = "";
split (LIST, srch, ",");
for (i = 1; i <= NF; i++) {
for (idx in srch) {
split ($i, arr, "=");
if (arr[1] == srch[idx]) {
printf sep "" arr[1] "=" arr[2];
sep = "^";
}
}
}
if (sep != "") {
print sep;
}
}'
1=A00^33=1^
1=a00^33=11^
This one allows you to use a single awk script and all you need to do is to provide a comma-separated list of keys to print out.
And here's the one-liner version :-)
echo '1=A00^35=D^150=1^33=1
1=a00^35=d^157=11^33=11
' | awk -vLST=1,33 -F^ '{s="";split(LST,k,",");for(i=1;i<=NF;i++){for(j in k){split($i,arr,"=");if(arr[1]==k[j]){printf s""arr[1]"="arr[2];s="^";}}}if(s!=""){print s;}}'
given a file 'in' containing your strings :
$ for i in $(cut -d^ -f2,3 < in);do echo $i^;done
35=D^150=1^
35=D^150=2^

Resources