Redirect output to the same input file in awk

Hi,

I want to compare a value from test file and redirect the o/p value to the same file

input file
250     32000   32      128

Below is my code

 
awk '{ if ($1 < "300") print $1 > /tmp/test}' test

want to compare 250 < 300 then print 300 to the same place

below is the required output

 
output file
300     32000   32      128

But the value (300) is not updating

Hello Stew,

Following may help you in same, following code will check if field 1st is less than 300 then it will print 300 at first field else it will print actual 1st field's values.

awk '{$1=$1<300?300:$1} 1'  Input_file

Output will be as follows.

300 32000 32 128

Also you can redirect this to a output file as per your requirement too.

Thanks,
R. Singh

awk '{ if ($1 < "300") { sub($1,"300",$0); print > "/tmp/test" } }' /tmp/test

Anbu -Thanks, your code is working. But I don't understand

sub ($1,"300"$0)

why you used $0 in target. I know sub will return either 0 or 1. I am bit confused.

sub($1,"300",$0) - Sub() replaces first field by 300 in $0 (entire line read from file test)

Anbu - your work for single line below is my actual i/p and o/p file

kernel.sem = 250   32000   32      128250
kernel.small = 1612364
kernel.shmmax = 2147483648

when I run

awk '/kernel.sem/ { if ($3 < "300") { sub($3,"300",$0); print > "/tmp/test" } }' /tmp/test

the entire file is replaced like below

kernel.sem = 300   32000   32      128250

my actual o/p file should be

kernel.sem = 300   32000   32      128250
kernel.small = 1612364
kernel.shmmax = 2147483648

if I run

awk '/kernel.sem/ {print $3,$6}' /tmp/test

, it prints

300 128250

correctly

Hello Stew,

Could you please try following and let me know if this helps.
Also my solution in POSt#2 also may help you in same.

awk '/kernel.sem/ { if ($3 < "300") { sub($3,"300",$0); print >> "/tmp/test1" } else {print >> "/tmp/test1" }}' /tmp/test

Here I am creating /tmp/test1 file as output, because file named /tmp/test is input file.

Hope this helps.

Thanks,
R. Singh

Ravinder, I do not want to send the o/p in a seprate file. since I should test in sysctl.conf file

Hello Stew,

If I understood you correctly you are trying to take OUTPUT or EDIT the same file which is being used for input as well, then I can say there no option present in awk to do so. You can create a script and execute like following.

cat script.ksh
awk '/kernel.sem/ { if ($3 < "300") { sub($3,"300",$0); print >> "/tmp/test1" } else {print >> "/tmp/test1" }}' ACTUAL_FILE
mv /tmp/test1 ACTUAL_FILE

Hope this helps.

Thanks,
R. Singh

you are correct. I want to edit the same file which being used for i/p file and update the o/p in the same i/p file.

My requirement is, need to edit kernel.sem values from sysctl.conf file. So I can't move the file name since the sysctl file is very sensitive.

Do you know any other option.

Hello Stew,

Could you please try the same and let me know if this helps, kindly try the same first with same details a test file
and if All is Well then you can try with actual file too.

cat script.ksh
VALUE=`awk '/kernel\.sem/ {if($3 < 300){print 1}}' Input_file`
if [[ $VALUE -eq 1 ]]
then
 sed -i '/kernel.sem/s/\(.*= \)\(...\)\(.*\)/\1 300 \3/g' Input_file
else
 echo "Values are fine nothing to do."
fi

Hope this helps.

Thanks,
R. Singh

I will try this . Thanks a lot for your help :slight_smile:

Hi,
Example standard procedure (here bash and awk):
Script:

$ cat script.bash
#!/usr/bin/bash

exec 3<demosysctl.conf
rm demosysctl.conf
awk '/kernel.sem/ { if ($3 < "300") { sub($3,"300",$0); print } ;next }1' <&3 >demosysctl.conf

Input:

$ cat demosysctl.conf
kernel.sem = 250   32000   32      128250
kernel.small = 1612364
kernel.shmmax = 2147483648

Execution:

$ ./script.bash
$ cat demosysctl.conf
kernel.sem = 300   32000   32      128250
kernel.small = 1612364
kernel.shmmax = 2147483648

Regards.

This is extremely dangerous code. Depending on the size of the input file and the location of the line being modified in the file, this may truncate your input file to size zero instead of updating a field in one or more lines in the file.

NEVER, try to read and write a file that might be larger than the size of the 1st read performed by awk or you will destroy your input file. (And, since there is no requirement placed on awk for the sizes of the reads it performs, NEVER, NEVER, NEVER do this.)

Using sed -i to update a system configuration file could also leave you with an unbootable system if your system loses power in the middle of an update. Ranvinder's suggestion in post #9 is much safer (using a temp file and moving or copying the updated file after it has been fixed), but it should verify that the awk command returned a zero exit status before attempting the mv on the next line.

However, it would be much better to create the temp file in the same directory as the file being updated (same filesystem is sufficient, but in case more file systems are created in the future, same directory is safer), so the mv is just a rename() which will always leave you with either the original configuration file or the updated configuartion file in place. (Of course, any change to a file could be corrupted by a disk block or controller going bad while you're making the update; but this method eliminates several other error cases that could destroy your data.)

Don - when I tried Ravichander post#9, print statement only printed the line what I edited

awk '/kernel.sem/ { if ($3 < "300") { sub($3,"300",$0); print >> "/tmp/test1" } else {print >> "/tmp/test1" }}' ACTUAL_FILE
 

please find the o/p below

 
/tmp/test1
kernel.sem = 300   32000   32      128250

So if I move this test1 to ACTUAL file, the file will be replaced with the content of test1.

DO you have any other option to achieve my requirement

First, be sure that you have a back-up copy of sysctl.conf before you try this. Then try something like:

nawk '
/kernel.sem/ {
	if(($3 + 0) < 300)
		sub($3, "300")
	else	exit(1)
}
1' sysctl.conf > sysctl.conf.$$ && \
	mv sysctl.conf.$$ sysctl.conf || \
	rm sysctl.conf.$$

Thanks Don. I have two doubts in your code

($3 + 0) - why you used + 0 in here
} 1' filename - why used 1 here, is it for prinitng purpose.

[quote="stew,post:17,topic:350679"]
Thanks Don. I have two doubts in your code

($3 + 0) - why you use

d + 0 in here
Because ($3 < "300")

performs a string comparison and would give you the wrong result if in some cases. For example, if the current contents of field 3 was 1000, it would reduce it to 300; and if the current contents of field 3 was 40, it wouldn't increase it to 300.

Yes, the default action in awk when a condition evaluates to true (non-zero) is to print the current line.

Did you try what I suggested? Did it give you the wrong results for any value on the kernel.sem line in your sysctl.conf file(s)?

yes. your code is working perfectly. I am using this sample peice for puppet automation. so for awk part I don't hard code anything. I will write all vlaues to some other yaml file and will ivoke that value variable into puppet awk function.

so my code would be

awk '/kernel.sem/ { if(($3 + 0) < $value) sub($3, "$value") } 1' /etc/sysctl.conf >> /etc/sysctl.conf.bkp

so what my doubt is, when I use $value parameter should I still use +0 on this

I have no idea what peice, puppet automation, vlaues, nor yaml mean, and there is no puppet() function in this awk script??? The awk variable value is not defined in this script and comparing the number in field 3 of a line to the entire line as a string (containing something like:

kernel.sem = 250   32000   32      128250

which is what $undefined_variable will expand to in your awk script with your sample input file) is not what you want. If you want to parameterize the threshold value for changes instead of hard coding it to 300, you need something like:

value=300
awk -v val="$value" '
/^kernel.sem/ {
	if(($3 + 0) < val)
		sub($3, val "")
	else	exit(1)
}
1' sysctl.conf > sysctl.conf.mod && \
	mv sysctl.conf.mod sysctl.conf || \
	rm sysctl.conf.mod

Please DO NOT use the name sysctl.conf.bkp in this script. It is not a backup file; it is a temp file used as a work area for this script. And, if the number found in the 3rd field on the "kernel.sem" line is greater than or equal to your value variable, it will only contain a subset of your file (since this awk script returns an error status and stops processing input if it is not going to change that line. This file will NEVER contain the original contents of your input file unless your input file does not contain a line that contains the string "kernel.sem"! This leaves the current sysctl.conf in place (instead of replacing it with a copy of itself) if no changes are being made, and runs faster than processing the rest of the file and replacing an unmodified file.

Note that I added an anchor to the ERE selecting the line to be changed in case your configuration file might contain a comment that explains why kernel.conf is being set.