Monday, November 30, 2009

Awk - extract negative numbers from file


Posting this entry so that awk newbies will see the use of awk NF,NR variables along with some basic awk 'for' and 'if constructs' use.

Input file:

$ cat file.txt
-5232,-92338,84545,34
-2233,25644,23233,2
6211,-1212,4343,43
-2434,621171,9121,-33

Required: Extract the numbers which starts with '-'

Solution using bash tr (command to translate or delete characters)

$ tr ',' '\n' < file.txt | grep ^-
Output:
-5232
-92338
-2233
-1212
-2434
-33

Using awk:

$ awk -F "," '
{for(i=1; i<=NF;i++)
if($i ~ /^-/) {printf "%s\n",$i}
}
' file.txt

Extending the above one liner to print the line number and field number where the negative value is present.

$ awk -F "," '
{for(i=1; i<=NF;i++)
if($i ~ /^-/) {
printf "Line # %s,Field # %s,Value = %s\n",NR,i,$i
}
}' file.txt

Output:
Line # 1,Field # 1,Value = -5232
Line # 1,Field # 2,Value = -92338
Line # 2,Field # 1,Value = -2233
Line # 3,Field # 2,Value = -1212
Line # 4,Field # 1,Value = -2434
Line # 4,Field # 4,Value = -33

Related posts:

- Awk for loop
- Awk if else
- Awk variables

6 comments:

Sergio said...

Alternate RS :)

awk 'BEGIN {RS=",|\n"}/^-/ {print}' file.txt

Unknown said...

@Voyeg3r, thank you so much, its a good one.

Anirudh said...

sed -e '
/,/y/,/\n/
/^[-]/P
D
'

Unknown said...

set -f

set -- $(< file.txt tr ',-' ' _')

dc -e "
# register 'a' prints TOS
[p]sa

# decrement function in register 'm'
[1 -]sm

# register 'c' pushes TOS into register 'r'
[Sr z 1 !>c]sc

# register 'd' pops register 'r' & prints if it is negative
[Lrd0>asx lklmxsk lk 1 !>d]sd

# starting stack state
$*

# store the stack length in register k
zsk

# pop off from stack & push into register r
lcx

## loop while register k >= 1
ldx
"

Unknown said...


if='file.txt'

< $if tr ',-' ' _' |
dc -e "
[q]sq
[Srlk1+sk[]]sN
[d0>Ns0z0<A]sA
[Lrps0lk1-dsk0<P]sP
[?z0=q0sklAxlPxl?x]s?
l?x
"

Unknown said...

Task: Print the line number and field number also where the negative value is present.

if='file.txt'
# "tr" converts the CSV-numbers to space-separated and also
# "dc" takes negative numbers as starting with "_" rather than "-"
< $if tr ',-' '\040_' \
dc -e "
[q]sq

# store the field number and corresp. negative number onto stack 'M'
[z SM SM []]sN

# recursive macro that picks up the negative numbers from the current line
[d 0 >N s0 z 0<A]sA

# the printing macro in the desired format
[ [line #]n l.n [, Field #]n n [, Value = ]n pc ]sP

# grab a pair (field num and negative num) from the stack 'M'
# then invoke the printing macro 'P' on the pair.
# then check by looking nondestructively at the TOS of 'M'
# whether we've reached the end. If not,
# recursively call itself for the next pair...
[LMLM lPx lM 0 !=B]sB

[? z 0 =q # grab next line & quit if found empty else do the below
l.1+s. # adjust line num counter
0SM # stuff stack ending number
lAx # grab all -ve nums on the current line
lBx # print all -ve nums on the current line in the approp. fmt.
l?x # rinse & recursively repeat...
]s?

# initialize line number counter '.'
0s.

# *** lights camera action! ***
l?x
"

© Jadu Saikia www.UNIXCL.com