0

Currently I'm using AWK to find and replace a portion of the first three occurrences of a string. The string is formatted as such, and there are many of these strings in the file:

func(tempID="39849235",count='12');

Using this link, I was able to find a method of using AWK to find and replace the first three instances of the string. I changed it to what I needed it to do, and a snippet of my script is below:

id=12349876
awk 'BEGIN {matches=0}
     matches < 3 && /.*tempID.*/ { sub(/tempID="[0-9]+"/,"tempID='"$id"'"); matches++ }
     { print $0 }' filName.py >filName.py.changed

The goal of the above code is to match on any line containing tempID and replace the number that is assigned to tempID with a value held in a variable named $id. The find and replace works well, but the one issue I seem to have is that no matter how I structure it, the output prints the $id without quotes. I've tried escaping quotes and putting single ticks, but regardless the line is changed to:

func(tempID=39849235,count='12');

I've tried removing the double quotes around the replace portion and structuring it as tempID="$id", but unfortunately this just replaces the ID number with the string $id.

Please let me know if there is a way to find and replace the tempID value and surround the value with quotes. I'm not stuck with AWK, so any other method with any other utility such as sed would work fine as well.

2 Answers 2

0

In your command there are quotes interpreted and stripped by the shell, there are quotes interpreted and stripped by awk, then you need quotes that would survive. You should escape them:

id=12349876
awk 'BEGIN {matches=0}
 matches < 3 && /.*tempID.*/ { sub(/tempID="[0-9]+"/,"tempID=\"'"$id"'\""); matches++ }
 { print $0 }' filName.py >filName.py.changed              # ^^  here ^^

Explanation. Your original command is like

awk 'BEGIN {matches=0} matches < 3 && /.*tempID.*/ { sub(/tempID="[0-9]+"/,"tempID='"$id"'"); matches++ } { print $0 }'
#   ^ these quotes are seen by the shell and don't get to awk                      ^^   ^^                            ^
#     these quotes get to awk and serve their purpose there      ^      ^  ^              ^
#     this variable is expanded by the shell and gets to awk as its value            ^^^

And this is the improved command:

awk 'BEGIN {matches=0} matches < 3 && /.*tempID.*/ { sub(/tempID="[0-9]+"/,"tempID=\"'"$id"'\""); matches++ } { print $0 }'
#   ^ these quotes are seen by the shell and don't get to awk                        ^^   ^^                              ^
#     these quotes get to awk and serve their purpose there      ^      ^  ^                  ^
#     these quotes will appear in the output thanks to being escaped                ^        ^
#     this variable is expanded by the shell and gets to awk as its value              ^^^

To reduce quoting frenzy you may use -v option to pass the variable to awk. Then you don't need to close and reopen single quotes mid-sequence only to let the shell expand $id. Instead unquoted (as awk sees it) id is expanded by awk on its own. Double quotes we need to add should be escaped as before:

id=12349876
awk -v id="$id" 'BEGIN {matches=0}
 matches < 3 && /.*tempID.*/ { sub(/tempID="[0-9]+"/,"tempID=\""id"\""); matches++ }
 { print $0 }' filName.py >filName.py.changed
1
  • Thank you very much! The awk -v was just what I needed! Dec 3, 2018 at 23:07
0

To achieve what you want to do, you need to:

For double quotes:

  • AWK: Escape double quotes " inside other double quotes.
    $1 $2 => foobar
    $1" __"$2"__" => foo __bar__
    $1" \""$2"\"" => foo "bar"

For single quotes:

  • Shell: Exit '…' your whole awk script is in, using another set of '…' inside it.
    'escaped 'unescaped' escaped'
    '$LINUX '$OSTYPE' $CPUTYPE' => $LINUX linux-gnu $CPUTYPE

  • Shell: Escape ' you want to be printed literally.
    'apostrophe that'\''s literal' => apostrophe that's literal

For example

echo foo bar | awk '{print "\""$1"\" '\''"$2"'\''"}'
"foo" 'bar'
1
  • Another way is with octal or hexadecimal codes inside double quotes echo foo bar | awk '{print "\x22"$1"\042","\x27"$2"\047"}'
    – Paulo
    Dec 3, 2018 at 23:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .