0

I am trying to insert variable text after another variable text on a specific line. Below is my attempt so far, as far as I can tell, if I can escape the quotations in awk so that the variable can be seen and still applied, then it should work, but I have no idea how to do this :

#!/bin/sh

VAR=$(head -n1 ~/Scripts/tmp/file.txt)
VAR1=$(head -n1 ~/Scripts/tmp/file1.txt)

awk '/$VAR/ { print; print "$VAR1"; next }1' ~/Scripts/tmp/file.txt
Rahul
  • 13,589
Giles
  • 897

3 Answers3

2

To pass strings to an awk script, pass them through environment variables.

export VAR VAR1
awk '
    1
    $0 == ENVIRON["VAR"] {print ENVIRON["VAR1"]}
' ~/Scripts/tmp/file.txt

I reorganized your script to make the logic simpler. I also replaced the regexp matching by a string comparison: with regexp matching, $VAR would be treated as a regular expression, it wouldn't perform a string comparison. And /$VAR/ wouldn't even use the value of VAR (that's Perl syntax, not awk), you'd need match($0, VAR) for that.

A note on some other solutions that don't really work (they work only if the variables don't contain any special character, which character is special depends on the method):

  • awk -v awkvar="$VAR" '$0 == awkvar …' expands backslashes in the line content.
  • awk "/$VAR/" … makes the shell expand the value of VAR as an awk snippet. For example, if file.txt contains ^/ {system("touch ~/naughty")} / then the command touch ~/naughty is executed.

An alternative approach is to make awk read all the files.

awk '
    BEGIN { VAR1 = getline <"~/Scripts/tmp/file1.txt"; }
    NR == 1 {VAR = $0}
    1
    $0 == ENVIRON["VAR"] {print ENVIRON["VAR1"]}
' ~/Scripts/tmp/file.txt
  • Thank you for the break down as well, as i'm still new and learning at the moment. Your correction worked perfectly, but i realised i need to add a tab between variable and variable 1, so have adapted it to: export VAR VAR1 awk ' 1 $0 == ENVIRON["VAR"] {print ENVIRON["VAR1"]} ' ORS=' ' ~/Scripts/tmp/file.txt > ~/Scripts/tmp/file2.txt – Giles May 19 '16 at 15:48
1

One possible AWK solution:

   awk '/'$VAR'/ { print; print "'$VAR1'"; next }1'

break the ' quotations around all your external variable.

Another possible solution like the other post suggest with passing variables is to pass VAR1 in as an awk variable but I think you still need to do the single quote block escape for the pattern using VAR:

 awk -v var=$VAR1 '/'$VAR'/ { print; print var; next }1'

You could try sed instead :) it is specifically designed for this sort of substitution.

 sed "s/$VAR.*/\0\n$VAR1/" ~/Scripts/tmp/file.txt     

if you can have multiple instances of VAR on a single line then add g at the end of the sed command (global) so that it will not stop at the first match it finds:

 sed "s/$VAR.*/\0 $VAR1/g" ~/Scripts/tmp/file.txt 

IMPORTANT!: make sure the '/' character does not appear in the VAR or VAR1 variable, if they can you need to escape them for awk or sed, alternatively with sed you can change the delimiter for the command to something else like ';' for example:

 sed "s;$VAR.*;\0\n$VAR1;" ~/Scripts/tmp/file.txt

Explanation of the sed command:
We use the double quote" instead of single quote around the sed command so that variables like VAR and VAR1 will be replaced by their values. This happens before the command is executed by sed which is why if the / can be present in the variable content that you need to address it (this is true for awk as well)
The 's' indicates that you writing a substitution command of the form:
s/<pattern>/<replacement pattern>/
The '/' immediately after the s is the delimiter that will be used to separate the sections of the command. so if you put ; you must use ; everywhere like shown above. The pattern to match will be the content of the $VAR variable. The substitution pattern is \0 which means print what was match by the pattern a new line '\n' and the content of VAR2.

OOPS did not see the part about line bellow.. fixed patterns.

Rob
  • 818
-1

If you want to use awk, you have to pass the bash variable to awk with the -v option :

awk -v v=$VAR -v v1=$VAR1 '/v/ { print; print v1; next }1' ~/Scripts/tmp/file.txt

But check Rob's answer for more correct and detailed solution.

magor
  • 3,752
  • 2
  • 13
  • 28
  • 3
    Did you test this before posting? Your advice about passing variables with -v is good, but the usages /$VAR/ and print "$var" will fail I think – steeldriver May 18 '16 at 13:20
  • 1
    Yep there are two variables, and you do not wnat the " or $ in front of var :) – Rob May 18 '16 at 13:26
  • ... and AFAIK you can't use variables inside a /.../ regex match: use $0 ~ var instead – steeldriver May 18 '16 at 13:29
  • You can break the '' block around your variables :) – Rob May 18 '16 at 13:31
  • 1
    yeah you are right. I haven't tested. corrected post – magor May 18 '16 at 13:38
  • @Rob great stuff, i didn't know that you can also use bash variables with double and simple quote – magor May 18 '16 at 13:39
  • 1
    You cannot :) you have to break the single quotes first.. if you look closely in my post you will see the " is before the break in the single quote block so that the awk script sees the " in its script, you then break the single quote block, use the terminal variable which will be textually replaced reopen the single quotes close the double quotes and finish the awk script :) Narly but it works :) – Rob May 18 '16 at 13:44
  • /v/ looks for lines containing the letter v, it has nothing to do with the variable v. You're missing double quotes around variable substitutions. awk -v v="$VAR" fails when $VAR contains backslashes, e.g. awk -v v="\\n" sets v to a newline. – Gilles 'SO- stop being evil' May 18 '16 at 21:38