Subtracting columns based on greater than in bash - unix

I have a file with multiple columns:
$ cat file.txt
a bc 34 67
t gh 68 -34
f jh -9 76
h in -66 -14
and so on
I am trying to extract when both columns are negative; when both are positive then subtract the two columns based on which value is greater; and if either column negative then add both the columns
For both negative its quite easy:
less file.txt | egrep -i "\-.*\-" | less
Expected Output:
h in -66 -14
For both positive I tried the following to no avail:
less file.txt | egrep -iv "\-.*\-" | awk '($3>$4 {print $0,($3-$4)}) || ($4>$3 {print $0,($4-$3)})' | less
Expected Output:
a bc 34 67 33
For either negative,
less file.txt | egrep -iv "\-.*\-" | awk '($3<0||$4<0) {print $0,($3+$4)}' | less
Expected Output:
t gh 68 -34 34
f jh -9 76 67
I am seeing this error:
awk: cmd. line:1: (FILENAME=- FNR=3208) fatal: print to "standard output" failed (Broken pipe)
egrep: write error
I know its a basic thing to do, any help would be appreciated!

One awk idea:
awk '
$3<0 && $4<0 { print $0 ; next }
$3>0 && $4>0 { print $0, ($3>=$4 ? $3-$4 : $4-$3); next }
{ print $0, $3+$4 }
' file.txt
NOTE: may need to be tweaked if $3==0 and/or $4==0 ... depends on OP's requirements for this scenario
This generates:
a bc 34 67 33
t gh 68 -34 34
f jh -9 76 67
h in -66 -14

Another awk implementation:
awk '
function abs(x) {
if (x < 0) x = -x
return x
}
$3 >= 0 || $4 >= 0 {$(NF+1) = abs(abs($3) - abs($4))}
{print}
' file.txt
a bc 34 67 33
t gh 68 -34 34
f jh -9 76 67
h in -66 -14
If you wanted to do this in plain bash:
abs() { echo $(($1 < 0 ? -($1) : $1)); }
while read -ra fields; do
a=${fields[2]}
b=${fields[3]}
if ((a >= 0 || b >= 0)); then
fields+=($(abs $(($(abs $a) - $(abs $b)))))
fi
echo "${fields[*]}"
done < file.txt

Related

AWK apply to all the rows except first few

I have a report xyz.rpt in the below format
# ajsh askj sdjh 54 12
# jhgj 765 2839
kjsd sdh sdsdf sdffsdf sdff
5464 765 67 65 76
2356 423 34 45 34
and so on
I want to print first 2 header line also along with the output of awk command.
awk '{if ( $5 < 65 ) print}' > aaa.rpt
One awk idea:
awk '
FNR<=2 # print 1st 2 lines
FNR>2 && $5+0==$5 && $5<65 # print lines where $5 is numeric and $5 < 65
' xyz.rpt
Or as a one-liner:
awk 'FNR<=2; FNR>2 && $5+0==$5 && $5 < 65' xyz.rpt
This generates:
ajsh askj sdjh 54 12
jhgj 765 2839
2356 423 34 45 34
not as elegant as i hoped
nawk '($5==+$(5*(+$5<=4^3)))+(3>FNR)'
ajsh askj sdjh 54 12
jhgj 765 2839
2356 423 34 45 34
$ awk 'NR<=2 || ($5+0==$5 && $5<65)' file
My solution would be:
awk 'BEGIN { for(i=0;i<2;i++) {getline; print;}}$5<65{print}' input
The BEGIN block is executed at the start. getline reads a line, which gets outputed.
$5<65 In stead of the if construction it's better to use a pattern.

group records based on field count unix

I'm having a tough time dealing with a file in Unix. Can someone give me directions on to how to deal with this? Thanks in advance
My file's field count is not consistent. I'm trying to gather all the records which has X number of fields.
when I did a awk check on the file based on the delimiter I found that a big % of my records having 19 fields. So I wanted to quarantine those records and make it a separate file.
file : x_orbit.txt
records : 1000
comma delimeter
cat x_orbit.txt | awk --field-separator=',' '{print NF}' | sort -n| uniq -c
rec col
700 19
50 20
50 21
50 22
50 23
10 24
10 25
10 26
10 27
10 28
9 29
1 31
1 32
1 33
1 35
10 36
27 42
awk '{print > ("out_"(NF==19 ? NF : "other"))}' x_orbit.txt

Print the file according to the pattern

Consider a file "test" containing following data:
REG NO: 123
20
24
REG NO: 124
30
70
Need to print the following:
123 20
123 24
124 30
124 70
The value need to be printed from different fields. And content of the heding column (regno ie 123,124) need to repeated for their marks.
In awk:
$ awk 'NF>1 {p=$NF;next} {print p, $1}' file
123 20
123 24
124 30
124 70
Explained:
$ awk '
NF>1 { # when a record has more than one field
p=$NF # store the last field to p var
next # proceed to next record
}
{
print p, $1 # otherwise output the p and the value on the record
}' file

Moving certain rows of a text file into a new one using shell

I have a tab delimited text file like
file 1
x1 23 47 59
x2 58 23 12
x3 39 30 11
...
x21909 020
and a simple list of values like
file 2
x1
x34
x56
x123
..
x9876
I'd like to take all of the rows in file 1 beginning with the values in file 2 and move them into a file 3, such that..
file 3
x1 23 47 69
x34 38 309 20
x56 49 201 10
x123 39 30 10
..
x9876 48 309 123
I've tried using the grep command but I'm not sure how to do it with a long list of values in file 2, and how to make it take the entire row. Is there a simple shell command which could do this?
With tabs as the delimiter, this should work:
for i in `cat file2`; do grep -P "^$i\t" file1; done
And with spaces as the delimiter, something like this should work:
for i in `cat file2`; do grep "^$i " file1; done
Cheers.
awk 'NR==FNR{a[$1]=$0;next} {print a[$1]}' file1 file2
if the first field values are distinct from the rest of the fields, grep fits the bill
grep -Ff file2 file1

Dynamically change what is being awked

I have this input below:
IDNO H1 H2 H3 HT Q1 Q2 Q3 Q4 Q5 M1 M2 EXAM
OUT OF 100 100 150 350 30 30 30 30 30 100 150 400
1434 22 95 135 252 15 20 12 18 14 45 121 245
1546 99 102 140 341 15 17 14 15 23 91 150 325
2352 93 93 145 331 14 17 23 14 10 81 101 260
(et cetera)
H1 H2 H3 HT Q1 Q2 Q3 Q4 Q5 M1 M2 EXAM
OUT OF 100 100 150 350 30 30 30 30 30 100 150 400
I need to use write a unix script to use the awk function to dynamically find any column that is entered in and have it displayed to the screen. I have successfully awked specific columns, but I cant seem to figure out how to make it change based on different columns. My instructor will simply pick a column for test data and my program needs to find that column.
what I was trying was something like:
#!/bin/sh
awk {'print $(I dont know what goes here)'} testdata.txt
EDIT: Sorry i should have been more specific, he is entering in the header name as the input. for example "H3". Then it needs to awk that.
I think you are just looking for:
#!/bin/sh
awk 'NR==1{ for( i = 1; i <= NF; i++ ) if( $i == header ) col=i }
{ print $col }' header=${1?No header entered} testdata.txt
This makes no attempt to deal with a column header that does not appear
in the input. (Left as an exercise for the reader.)
Well, you question is quite diffuse and in principle you want someone else write your awk script... You should check man pages for awk, they are quite descriptive.
my 2 cent wort, as an example would be (for the row) :
myscript.sh:
#/bin/sh
cat $1 | awk -v a=$2 -v b=$3 '{if ($(a)==b){print $0}}';
if you just want a column, well,
#/bin/sh
cat $1 | awk -v a=$2 '{print $(a)}';
Your input would be :
myscript.sh file_of_data col_num
Again, reiterating: Please study man pages of awk. Also please when asking question present what you have tried (code) and errors (logs). This will make people more ready to help you.
Your line format has a lot of variation (in the number of fields). That said, what about something like this:
echo "Which column name?"
read column
case $column in
(H1) N=2;;
(H2) N=3;;
(H3) N=4;;
...
(*) echo "Please try again"; exit 1;;
esac
awk -v N=$N '{print $N}' testdata.txt

Resources