I have the following script:
script.sh:
#!/bin/bash
FileName=$1;
awk '
/Import()/
{
while (sub(/\$requestedMessage/, "bar") == 0)
{
print;
getline;
}
}
1' > file.tmp $FileName
And the following file:
file:
function FunctionA()
{
message=$requestedMessage
}
function FunctionB()
{
message=$requestedMessage
}
The idea is to call
./script.sh file
and to replace the message=$requestedMessage variable in function FunctionB() to message=bar but only in FunctionB() and not FunctionA()
I am very new to AWK and I don't seem to understand what I am doing wrong.
Thanks,
awk -v RS= -v ORS='\n\n' '
/FunctionB\(\)/ {
sub(/\$requestedMessage/, "bar")
}
1' "$FileName" > file
Maybe this is more like what you had in mind:
awk '
/^function/ { inTarget = (/FunctionB\(\)/ ? 1 : 0) }
inTarget { sub(/\$requestedMessage/, "bar") }
1' "$FileName" > file
If not - how do you know when you reach the end of a function definition? The above assumes you know one function has ended when the next function definition begins.
Related
I have an awk script (tst.awk):
NR==FNR {
ids[++numIds] = $1","
next
}
FNR==1 { numFiles++ }
{
id = $1
sub(/^[^[:space:]]+[[:space:]]+/,"")
vals[id,numFiles] = $0
gsub(/[^[:space:],]+/,"NA")
naVal[numFiles] = $0
}
END {
for ( idNr=1; idNr<=numIds; idNr++) {
id = ids[idNr]
printf "%s%s", id, OFS
for (fileNr=1; fileNr<=numFiles; fileNr++) {
val = ((id,fileNr) in vals ? vals[id,fileNr] : naVal[fileNr])
printf "%s%s", val, (fileNr<numFiles ? OFS : ORS)
}
}
}
That is called on the command line with:
awk -f tst.awk master file1 file2 file3 > output.file
(note: there can be a variable number of arguments)
How can I change this script, and command line code, to run it as a bash script?
I have tried (tst_awk.sh):
#!/bin/bash
awk -f "$1" "$2" "$3" "$4"
'NR==FNR {
ids[++numIds] = $1","
next
}
FNR==1 { numFiles++ }
{
id = $1
sub(/^[^[:space:]]+[[:space:]]+/,"")
vals[id,numFiles] = $0
gsub(/[^[:space:],]+/,"NA")
naVal[numFiles] = $0
}
END {
for ( idNr=1; idNr<=numIds; idNr++) {
id = ids[idNr]
printf "%s%s", id, OFS
for (fileNr=1; fileNr<=numFiles; fileNr++) {
val = ((id,fileNr) in vals ? vals[id,fileNr] : naVal[fileNr])
printf "%s%s", val, (fileNr<numFiles ? OFS : ORS)
}
}
}' > output_file
called on command line with:
./tst_awk.sh master file1 file2 file3
I have also tried (tst_awk2.sh):
#!/bin/bash
awk -f master file1 file2 file3
'NR==FNR {
ids[++numIds] = $1","
next
}
FNR==1 { numFiles++ }
...
}
}
}' > output_file
called on command line with:
./tst_awk2.sh
-f needs to be followed by the name of the awk script. You're putting the first argument of the shell script after it.
You can use "$#" to get all the script arguments, so you're not limited to just 4 arguments.
#!/bin/bash
awk -f /path/to/tst.awk "$#" > output_file
Use an absolute path to the awk script so you can run the shell script from any directory.
If you don't want to use the separate tst.awk, you just put the script as the literal first argument to awk.
#!/bin/bash
awk 'NR==FNR {
ids[++numIds] = $1","
next
}
FNR==1 { numFiles++ }
{
id = $1
sub(/^[^[:space:]]+[[:space:]]+/,"")
vals[id,numFiles] = $0
gsub(/[^[:space:],]+/,"NA")
naVal[numFiles] = $0
}
END {
for ( idNr=1; idNr<=numIds; idNr++) {
id = ids[idNr]
printf "%s%s", id, OFS
for (fileNr=1; fileNr<=numFiles; fileNr++) {
val = ((id,fileNr) in vals ? vals[id,fileNr] : naVal[fileNr])
printf "%s%s", val, (fileNr<numFiles ? OFS : ORS)
}
}
}' "$#" > output_file
you can make your awk script executable by adding the shebang
#! /bin/awk -f
NR==FNR {
ids[++numIds] = $1","
next
}...
don't forget to chmod +x tst.awk
and run
$ ./tst.awk master file1 file2 file3 > outfile
New to StackExchange, forgive me for errors.
I have an input file that needs to be copied into another file, before the last character.
inputfile.txt:
input {
"inputstuff"
}
filetowriteto.txt:
Stuff {
foo {
"foostuff"
}
bar {
"barstuff"
}
}
After running the script, the resulting file should now be:
filetowriteto.txt:
Stuff {
foo {
"foostuff"
}
bar {
"barstuff"
}
input {
"inputstuff"
}
}
Basically the script copies the input set of lines and pastes them just before the last right bracket in filetowriteto.txt.
The script can't rely on line counts, since filetowriteto.txt doesn't have a predicable amount of foo or bar lines, and I don't know how to use sed or awk to do this.
Try:
$ awk 'FNR==NR{ if (NR>1) print last; last=$0; next} {print " " $0} END{print last}' filetowriteto.txt inputfile.txt
Stuff {
foo {
"foostuff"
}
bar {
"barstuff"
}
input {
"inputstuff"
}
}
To change the file in place:
awk 'FNR==NR{ if (NR>1) print last; last=$0; next} {print " " $0} END{print last}' filetowriteto.txt inputfile.txt >tmp && mv tmp filetowriteto.txt
How it works
FNR==NR{ if (NR>1) print last; last=$0; next}
While reading the first file, (a) if we are not on the first line, print the value of last, (b) assign the text of the current line to last, and (c) skip the rest of the commands and jump to the next line.
This uses a common awk trick. The condition FNR==NR is only true while we are reading the first file. This is because, in awk, NR is the number of lines that we have read so far while FNR is the number of lines that we have read so far from the current file. Thus, FNR==NR is only true when we are reading from the first file.
print " " $0
While reading the second file, print each line with some leading white space.
END{print last}
After we have finished printing the second file, print the last line of the first file.
Given:
$ cat /tmp/f1.txt
input {
"inputstuff"
}
$ cat /tmp/f2.txt
Stuff {
foo {
"foostuff"
}
bar {
"barstuff"
}
}
You can use command grouping to achieve this:
$ ( sed '$d' f2.txt ; cat f1.txt ; tail -n1 f2.txt )
or (this version does not create a sub shell)
$ { sed '$d' f2.txt ; cat f1.txt ; tail -n1 f2.txt; }
How does it work?
sed '$d' f2.txt prints all but the last line of f2.txt
cat f1.txt prints f1.txt at that point
tail -n1 f2.txt print the last line of f2 now
If you want to indent f1.txt, use sed instead of cat. You can also use sed to print the last line:
$ { sed '$d' /tmp/f2.txt ; sed 's/^/ /' /tmp/f1.txt ; sed -n '$p' /tmp/f2.txt; }
Stuff {
foo {
"foostuff"
}
bar {
"barstuff"
}
input {
"inputstuff"
}
}
And then you can redirect the output of the grouping to a file if you wish with a > redirect.
$ cat tst.awk
NR==FNR {
rec = (NR>1 ? rec ORS : "") $0
next
}
FNR>1 {
print prev
if ( sub(/[^[:space:]].*/,"",prev) ) {
indent = prev
}
}
{ prev=$0 }
END {
gsub(ORS,ORS indent,rec)
print indent rec ORS prev
}
$ awk -f tst.awk inputfile.txt filetowriteto.txt
Stuff {
foo {
"foostuff"
}
bar {
"barstuff"
}
input {
"inputstuff"
}
}
The above uses the indentation from the last non-blank line before the last line of the file to be modified to set the indentation for the new file it's inserting.
Suppose i have a very file which i created from two files one is old & another is the updated file by using cat & sort on the primary key.
File1
102310863||7097881||6845193||271640||06007709532577||||
102310863||7097881||6845123||271640||06007709532577||||
102310875||7092992||6840808||023740||10034500635650||||
102310875||7092992||6840818||023740||10034500635650||||
So pattern of this file is line 1 = old value & line 2 = updated value & so on..
now I want to process the file in such a way that awk first process the first two lines of the file & find out the difference & then move on two the next two lines.
now the process is
if($[old record]!=$[new record])
i= [new record]#[old record];
Desired output
102310863||7097881||6845123#6845193||271640||06007709532577||||
102310875||7092992||6840818#6840808||023740||10034500635650||||
$ cat tst.awk
BEGIN { FS="[|][|]"; OFS="||" }
NR%2 { split($0,old); next }
{
for (i=1;i<=NF;i++) {
if (old[i] != $i) {
$i = $i "#" old[i]
}
}
print
}
$
$ awk -f tst.awk file
102310863||7097881||6845123#6845193||271640||06007709532577||||
102310875||7092992||6840818#6840808||023740||10034500635650||||
This awk could help:
$ awk -F '\\|\\|' '{
getline new;
split(new, new_array, "\\|\\|");
for(i=1;i<=NF;i++) {
if($i != new_array[i]) {
$i = new_array[i]"#"$i;
}
}
} 1' OFS="||" < input_file
102310863||7097881||6845123#6845193||271640||06007709532577||||
102310875||7092992||6840818#6840808||023740||10034500635650||||
I think, you are good enough in awk to understand above code. Skipping the explanation.
Updated version, and thanks #martin for the double | trick:
$ cat join.awk
BEGIN {new=0; FS="[|]{2}"; OFS="||"}
new==0 {
split($0, old_data, "[|]{2}")
new=1
next
}
new==1 {
split($0, new_data, "[|]{2}")
for (i = 1; i <= 7; i++) {
if (new_data[i] != old_data[i]) new_data[i] = new_data[i] "#" old_data[i]
}
print new_data[1], new_data[2], new_data[3], new_data[4], new_data[5], new_data[6], new_data[7]
new = 0
}
$ awk -f join.awk data.txt
102310863||7097881||6845123#6845193||271640||06007709532577||||
102310875||7092992||6840818#6840808||023740||10034500635650||||
My code 1 :
awk -F'|' -v PARM_VAL="${PARM_VALUE[*]}" '
BEGIN { split(PARM_VAL,pa," ")
fn_1()
{
print "inside fn"
}
}
FNR==NR{ for(i=1;i<=NF;i++) a[NR,i]=$i; }
{if (FILENAME == "SPP_OUT") {print $1}}
fn_1
END {printf " second value of SPPIN : "a[2,2]} ' SPP_IN SPP_OUT
I am getting error fatal: function `fn_1' not defined
My code 2 :
awk -F'|' -v PARM_VAL="${PARM_VALUE[*]}" '
BEGIN { split(PARM_VAL,pa," ")
fn_1()
{
ret = "returned"
return ret
}
}
FNR==NR{ for(i=1;i<=NF;i++) a[NR,i]=$i; }
{if (FILENAME == "SPP_OUT") {print $1}}
m=fn_1()
END {printf " second value of SPPIN : "a[2,2];print $m} ' SPP_IN SPP_OUT
I am facing
awk: cmd. line:6: return ret
awk: cmd. line:6: ^ `return' used outside function context
Can any asssist ?
Thanks
The function should be defined out of the BEGIN block. For example:
$ cat function.awk
function fib(n, n_1, n_2)
{
if (n < 2) {
return n
} else {
n_1 = fib(n - 1)
n_2 = fib(n - 2)
return n_1 + n_2
}
}
BEGIN {
for (i = 0; i < 5; ++i) {
printf("fib(%d) = %d\n", i, fib(i));
}
}
$ awk -f function.awk
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
$
The user-defined awk function syntax is
function NAME(PARAMETER-LIST)
{
BODY-OF-FUNCTION
}
The tricky part is:
PARAMETER-LIST is a list of the function's arguments and local variable
names, separated by commas. When the function is called, the argument
names are used to hold the argument values given in the call. The
local variables are initialized to the empty string. A function cannot
have two parameters with the same name, nor may it have a parameter
with the same name as the function itself.
See the awk manual for more details.
To get the nucleus of your code working I had to add the function keyword when defining the function, and parentheses when making the call like so:
$ cat foo.awk
BEGIN { print "begin" }
function fn_1()
{
print "inside fn"
}
{
fn_1()
}
END { print "end" }
$ echo 'xyz' | awk -f foo.awk
begin
inside fn
end
From the awk manual:
The definition of a function named name looks like this:
function name(parameter-list)
{
body-of-function
}
How do I print in a .log file using awk command inside a ksh file?
The script goes this way:
##create file here
## Start process
awk 'BEGIN {
some code here
}
{
##Logic here
##Print to file
}
END {}
' $OUTPUTNEEDEDTOBEPRINTED
You can redirect within the awk code:
##create file here
## Start process
awk 'BEGIN {
some code here
}
{
print > "myfile"
}
END {}
' "$OUTPUTNEEDEDTOBEPRINTED"
Or just redirect the output of you awk
##create file here
## Start process
awk 'BEGIN {
some code here
}
{
##Logic here
##Print to file
}
END {}
' "$OUTPUTNEEDEDTOBEPRINTED" > "myfile"