I was thinking about this problem few days ago and realized it could probably be solved with a simple awk script. After a little bit of playing around and short testing, I found this oneliner to do the trick:
awk 'BEGIN{OFS="\t"} !/#/ {sub(/\./, $1"_"$2, $3)}1' input.vcf > annotated_input.vcf
However I tested it only briefly, so use it with caution. If you find some issue with this oneliner, please let me know, either here or by commenting under the gist on github.
The oneliner works like this:
BEGIN{OFS="\t"}= output will be tab-delimited!/#/= skip comment lines containing#(i.e. print them unmodified){sub(/\./, $1"_"$2, $3)}= substitute.in third column ($3, i.e. the ID column) with combination of$1and$2(i.e. CHROM and POS), separated by "_"1= This condition (always true) prints all lines, modified or not.
FYI: Both the original script and this oneliner assume that your variants have unique positions, i.e. you don't have the same CHROM & POS combo on more than one row (this could be violated e.g. after decomposing complex variants). If your VCF does have this issue, the scripts would need to be updated (one way would be to merge not just CHROM & POS columns, but to include also e.g. ALT column, so the ID would be CHROM_POS_ALT). Again, if you need that, let me know and I can show how to do that (if it's not clear from the code itself).