text: user4424: In f1 below I am trying to split $1 based on each line and create a bed file. If the line is a snp then the : is spilt and the text is $1 the last digit is $2 -1 and $3 and the letter to the left of the > is $4 and the letter to the right is $5. (lines 1 and 2)
  If the line has dup then the : is spilt and the text is $1 the last digit is $2 and $3 and the $4 is - and the letter to the right is $5. (line 4)
  If the line has del with nothing else then the : is spilt and the text is $1 the last digit is $2 and $3 and the letter to the left of the > is $4 and $5 is -. (line 5)
  I am not sure how to format for line 3 (the complex indel)... maybe split $1 on the : and the text is $1 the last digit is $2 and $3 and the letters to the left of the > is $4 and the letters to the right is $5. Thank you :)
  f1
  chr7:140453145A>T
  chr7:140453136A>T 
  chr7:140453135_140453136delCAinsTT
  chr20:31022287dupA
  chr19:13054614delG
  
  desired
  chr7    140453144   140453145   A   T 
  chr7    140453135   140453136   A   T 
  chr7    140453135   140453136   TT  CA
  chr20   31022287    31022287    -   A
  chr19   13054614    13054614    G   -
  
  awk
  awk 'BEGIN{OFS="\t"} {sub(/[^0-9]+$/, "", $1)}' f1
  
  awk 'BEGIN { FS = "[ -]"; OFS="\t" } NF==3 { print $1, $2 - 1, $2, $3 } NF==4 { print $1, $2, $3, $4 }' f1
  
  
  user3541: Here is an R solution, would probably be slower than the suggested Python solution though:
  bed <- readLines("bed.txt")
  
  res <- data.frame()
  
  for(i in bed) {
  
    temp <- strsplit(i, ":") %>% unlist()
  
    # case N>N
  
    if(grepl(">", temp[[2]])) {
  
      begin <- str_extract(temp[[2]], pattern = "^[:digit:]+") %>% as.integer() - 1
      end <- begin + 1
      ref <- str_extract(temp[[2]], pattern = "[A-Z]")
      alt <- str_extract(temp[[2]], pattern = "[A-Z]$")
  
      temp_res <- data.frame(temp[[1]], begin, end, ref, alt)
      res <- rbind(res, temp_res)
    }
  
    # case del_ins
  
    if(grepl("ins", temp[[2]])) {
  
      begin <- str_extract(temp[[2]], pattern = "^[:digit:]+") %>% as.integer()
      end <- str_extract(temp[[2]], pattern = "_[:digit:]+")
      end <- substring(end, 2, nchar(end)) %>% as.integer()
      ref <- str_extract(temp[[2]], pattern = "[A-Z]+$")
      alt <- str_extract(temp[[2]], pattern = "[A-Z]+")
  
      temp_res <- data.frame(temp[[1]], begin, end, ref, alt)
      res <- rbind(res, temp_res)
    }
  
  
    # case dup
  
    if(grepl("dup", temp[[2]])) {
  
      begin <- str_extract(temp[[2]], pattern = "^[:digit:]+") %>% as.integer()
      end <- begin
      ref <- "-"
      alt <- str_extract(temp[[2]], pattern = "[A-Z]$")
  
      temp_res <- data.frame(temp[[1]], begin, end, ref, alt)
      res <- rbind(res, temp_res)
    }
  
    # case del only
  
    if(grepl("del", temp[[2]]) & !grepl("ins", temp[[2]])) {
  
      begin <- str_extract(temp[[2]], pattern = "^[:digit:]+") %>% as.integer()
      end <- begin
      ref <- str_extract(temp[[2]], pattern = "[A-Z]$")
      alt <- "-"
  
      temp_res <- data.frame(temp[[1]], begin, end, ref, alt)
      res <- rbind(res, temp_res)
    }
  
  }
  
  print(res)
  
    temp..1..     begin       end ref alt
  1      chr7 140453144 140453145   A   T
  2      chr7 140453135 140453136   A   T
  3      chr7 140453135 140453136  TT  CA
  4     chr20  31022287  31022287   -   A
  5     chr19  13054614  13054614   G   -
  
  
  user96: The Python script in my first response is heavy on regex matching, which is pretty clunky in Python. I like Python much better than Perl overall, but a throwaway script like this will be clearer and more concise in Perl.
  #!/usr/bin/env perl
  use strict;
  
  while(<STDIN>) {
      if (m/(\S+):(\d+)_(\d+)del([ACGT]+)ins([ACGT]+)/) {
          print("$1\t$2\t$3\t$5\t$4\n");
      }
      elsif (m/(\S+):(\d+)(del|dup)([ACGT]+)/) {
          if ($3 eq "del") {
              print("$1\t$2\t$2\t$4\t-\n");
          }
          else {
              print("$1\t$2\t$2\t-\t$4\n");
          }
      }
      elsif (m/(\S+):(\d+)([ACGT]+)>([ACGT]+)/) {
          my $coord2 = $2;
          my $coord1 = $2 - 1;
          print("$1\t$coord1\t$coord2\t$3\t$4\n")
      }
  }
  
  It's been several years since I wrote Perl on a regular basis, so there are probably ways to make the script even more clear and concise. (Although I have a love/hate relationship with the default/hidden variables, which can make a script more concise but also harder for a newcomer to understand.)
  Invoked like so on the command line.
  [standage@lappy ~] $ ./transform < f1
  chr7 140453144 140453145 A T
  chr7 140453135 140453136 A T
  chr7    140453135   140453136   TT  CA
  chr20   31022287    31022287    -   A
  chr19   13054614    13054614    G   -
  
  
  user73: Quick Perl solution (following on from Daniel's answer saying that this would be "clearer and more concise in Perl"). I was a bit confused with your BED output because you've treated the start/end locations of a single base deletion and insertion different to a SNP. In my example here, the start and end locations for those cases are the same to distinguish from the two-base change for TT->CA [this can be changed]:
  cat 10454.txt | \
  perl -ne 'if(/^(.*?):([0-9]+)(_([0-9]+))?(del|dup)?([ACGT]+)((ins|>)([ACGT]+))?/){
     print(join("\t",
           $1,
           $2,
           $4?$4:($2),
           ($8 eq "ins")?$9:($5 eq "dup")?"-":$6,
           (($5 eq "dup") || ($8 eq "ins"))?$6:($9)?$9:"-")."\n")
  }';
  chr7    140453145       140453145       A       T
  chr7    140453136       140453136       A       T
  chr7    140453135       140453136       TT      CA
  chr20   31022287        31022287        -       A
  chr19   13054614        13054614        G       -
  
  Regular Expression
  This uses enclosing groups to describe the important bits of the sequence. The groups are numbered from 1 upwards, with each number corresponding to a left bracket (in order), capturing the entirety of the sub-expression until the paired right bracket. I'll describe the groups, which are referenced in the subsequent part of the script as $<group_number>:
  
  Any number of characters, up to (but not including) the first ':'
  Any number of the characters '0' to '9' [i.e. numbers]
  Optionally, a "_", followed by any number of the characters '0' to '9'
  The numbers from Group [3], i.e. excluding the "_"
  Optionally, a "dup" or a "del"
  Any number of characters selected from "A", "C", "G", "T" (i.e. bases)
  Optionally, an "ins" or ">", followed by any number of bases
  The "ins" or ">" from Group [7]
  The bases from Group [7]
  
  Print Code
  The print statement prints out a tab-separated list, finishing off with a line break. I'll describe each list element:
  
  Group [1]
  Group [2]
  If Group [4] exists, then Group [4], otherwise Group [2]
  If Group [8] is "ins", then Group [9]; [otherwise]
  If Group [5] is "dup", then "-", otherwise Group [6]
  If Group [5] is "dup", or group [8] is "ins", then Group [6][otherwise]
  If Group [9] exists, then Group [9], otherwise "-"
  
  
  user96: The following Python script seems to do the job.
  #!/usr/bin/env python3
  
  from __future__ import print_function
  import re
  import sys
  
  for line in sys.stdin:
      if '_' in line:
          match = re.search(r'(\S+):(\d+)_(\d+)del([ACGT]+)ins([ACGT]+)', line)
          print(match.group(1), match.group(2), match.group(3), match.group(5), match.group(4), sep='\t')
      elif 'dup' in line or 'del' in line:
          match = re.search(r'(\S+):(\d+)(del|dup)([ACGT]+)', line)
          if match.group(3) == 'del':
              print(match.group(1), match.group(2), match.group(2), match.group(4), '-', sep='\t')
          else:
              print(match.group(1), match.group(2), match.group(2), '-', match.group(4), sep='\t')
      else:
          match = re.search(r'(\S+):(\d+)([ACGT]+)>([ACGT]+)', line)
          coord = int(match.group(2))
          print(match.group(1), coord - 1, coord, match.group(3), match.group(4), sep='\t')
  
  Invoked like so on the command line.
  [standage@lappy ~] $ ./transform < f1
  chr7 140453144 140453145 A T
  chr7 140453135 140453136 A T
  chr7    140453135   140453136   TT  CA
  chr20   31022287    31022287    -   A
  chr19   13054614    13054614    G   -
  
  
original_text: 4424: <p>In <code>f1</code> below I am trying to split <code>$1</code> based on each line and create a bed file. If the line is a snp then the <code>:</code> is spilt and the text is <code>$1</code> the last digit is <code>$2</code> -1 and <code>$3</code> and the letter to the left of the <code>&gt;</code> is <code>$4</code> and the letter to the right is <code>$5</code>. (lines 1 and 2)</p>
  
  <p>If the line has <code>dup</code> then the <code>:</code> is spilt and the text is <code>$1</code> the last digit is <code>$2</code> and <code>$3</code> and the <code>$4</code> is <code>-</code> and the letter to the right is <code>$5</code>. (line 4)</p>
  
  <p>If the line has <code>del</code> with nothing else then the <code>:</code> is spilt and the text is <code>$1</code> the last digit is <code>$2</code> and <code>$3</code> and the letter to the left of the <code>&gt;</code> is <code>$4</code> and <code>$5</code> is <code>-</code>. (line 5)</p>
  
  <p>I am not sure how to format for line 3 (the complex indel)... maybe split <code>$1</code> on the <code>:</code> and the text is <code>$1</code> the last digit is <code>$2</code> and <code>$3</code> and the letters to the left of the <code>&gt;</code> is <code>$4</code> and the letters to the right is <code>$5</code>. Thank you :)</p>
  
  <p><strong>f1</strong></p>
  
  <pre><code>chr7:140453145A&gt;T
  chr7:140453136A&gt;T 
  chr7:140453135_140453136delCAinsTT
  chr20:31022287dupA
  chr19:13054614delG
  </code></pre>
  
  <p><strong>desired</strong></p>
  
  <pre><code>chr7    140453144   140453145   A   T 
  chr7    140453135   140453136   A   T 
  chr7    140453135   140453136   TT  CA
  chr20   31022287    31022287    -   A
  chr19   13054614    13054614    G   -
  </code></pre>
  
  <p><strong>awk</strong></p>
  
  <pre><code>awk 'BEGIN{OFS="\t"} {sub(/[^0-9]+<span class="math-container">$/, "", $</span>1)}' f1
  
  awk 'BEGIN { FS = "[ -]"; OFS="\t" } NF==3 { print <span class="math-container">$1, $</span>2 - 1, <span class="math-container">$2, $</span>3 } NF==4 { print <span class="math-container">$1, $</span>2, <span class="math-container">$3, $</span>4 }' f1
  </code></pre>
   3541: <p>Here is an <code>R</code> solution, would probably be slower than the suggested <code>Python</code> solution though:</p>
  
  <pre><code>bed &lt;- readLines("bed.txt")
  
  res &lt;- data.frame()
  
  for(i in bed) {
  
    temp &lt;- strsplit(i, ":") %&gt;% unlist()
  
    # case N&gt;N
  
    if(grepl("&gt;", temp[[2]])) {
  
      begin &lt;- str_extract(temp[[2]], pattern = "^[:digit:]+") %&gt;% as.integer() - 1
      end &lt;- begin + 1
      ref &lt;- str_extract(temp[[2]], pattern = "[A-Z]")
      alt &lt;- str_extract(temp[[2]], pattern = "[A-Z]$")
  
      temp_res &lt;- data.frame(temp[[1]], begin, end, ref, alt)
      res &lt;- rbind(res, temp_res)
    }
  
    # case del_ins
  
    if(grepl("ins", temp[[2]])) {
  
      begin &lt;- str_extract(temp[[2]], pattern = "^[:digit:]+") %&gt;% as.integer()
      end &lt;- str_extract(temp[[2]], pattern = "_[:digit:]+")
      end &lt;- substring(end, 2, nchar(end)) %&gt;% as.integer()
      ref &lt;- str_extract(temp[[2]], pattern = "[A-Z]+$")
      alt &lt;- str_extract(temp[[2]], pattern = "[A-Z]+")
  
      temp_res &lt;- data.frame(temp[[1]], begin, end, ref, alt)
      res &lt;- rbind(res, temp_res)
    }
  
  
    # case dup
  
    if(grepl("dup", temp[[2]])) {
  
      begin &lt;- str_extract(temp[[2]], pattern = "^[:digit:]+") %&gt;% as.integer()
      end &lt;- begin
      ref &lt;- "-"
      alt &lt;- str_extract(temp[[2]], pattern = "[A-Z]$")
  
      temp_res &lt;- data.frame(temp[[1]], begin, end, ref, alt)
      res &lt;- rbind(res, temp_res)
    }
  
    # case del only
  
    if(grepl("del", temp[[2]]) &amp; !grepl("ins", temp[[2]])) {
  
      begin &lt;- str_extract(temp[[2]], pattern = "^[:digit:]+") %&gt;% as.integer()
      end &lt;- begin
      ref &lt;- str_extract(temp[[2]], pattern = "[A-Z]$")
      alt &lt;- "-"
  
      temp_res &lt;- data.frame(temp[[1]], begin, end, ref, alt)
      res &lt;- rbind(res, temp_res)
    }
  
  }
  
  print(res)
  
    temp..1..     begin       end ref alt
  1      chr7 140453144 140453145   A   T
  2      chr7 140453135 140453136   A   T
  3      chr7 140453135 140453136  TT  CA
  4     chr20  31022287  31022287   -   A
  5     chr19  13054614  13054614   G   -
  </code></pre>
   96: <p>The Python script in my first response is heavy on regex matching, which is pretty clunky in Python. I like Python much better than Perl overall, but a throwaway script like this will be clearer and more concise in Perl.</p>
  
  <pre class="lang-perl prettyprint-override"><code>#!/usr/bin/env perl
  use strict;
  
  while(&lt;STDIN&gt;) {
      if (m/(\S+):(\d+)_(\d+)del([ACGT]+)ins([ACGT]+)/) {
          print("<span class="math-container">$1\t$</span>2\t<span class="math-container">$3\t$</span>5\t<span class="math-container">$4\n");
      }
      elsif (m/(\S+):(\d+)(del|dup)([ACGT]+)/) {
          if ($3 eq "del") {
              print("$1\t$2\t$2\t$4\t-\n");
          }
          else {
              print("$1\t$2\t$2\t-\t$4\n");
          }
      }
      elsif (m/(\S+):(\d+)([ACGT]+)&gt;([ACGT]+)/) {
          my $coord2 = $2;
          my $coord1 = $2 - 1;
          print("$1\t$coord1\t$coord2\t$3\t$</span>4\n")
      }
  }
  </code></pre>
  
  <p>It's been several years since I wrote Perl on a regular basis, so there are probably ways to make the script even more clear and concise. (Although I have a love/hate relationship with the default/hidden variables, which can make a script more concise but also harder for a newcomer to understand.)</p>
  
  <p>Invoked like so on the command line.</p>
  
  <pre><code>[standage@lappy ~] $ ./transform &lt; f1
  chr7 140453144 140453145 A T
  chr7 140453135 140453136 A T
  chr7    140453135   140453136   TT  CA
  chr20   31022287    31022287    -   A
  chr19   13054614    13054614    G   -
  </code></pre>
   73: <p>Quick Perl solution (following on from Daniel's answer saying that this would be "clearer and more concise in Perl"). I was a bit confused with your BED output because you've treated the start/end locations of a single base deletion and insertion different to a SNP. In my example here, the start and end locations for those cases are the same to distinguish from the two-base change for TT->CA [this can be changed]:</p>
  
  <pre><code>cat 10454.txt | \
  perl -ne 'if(/^(.*?):([0-9]+)(_([0-9]+))?(del|dup)?([ACGT]+)((ins|&gt;)([ACGT]+))?/){
     print(join("\t",
           <span class="math-container">$1,
           $</span>2,
           <span class="math-container">$4?$</span>4:(<span class="math-container">$2),
           ($</span>8 eq "ins")?<span class="math-container">$9:($</span>5 eq "dup")?"-":<span class="math-container">$6,
           (($</span>5 eq "dup") || (<span class="math-container">$8 eq "ins"))?$</span>6:(<span class="math-container">$9)?$</span>9:"-")."\n")
  }';
  chr7    140453145       140453145       A       T
  chr7    140453136       140453136       A       T
  chr7    140453135       140453136       TT      CA
  chr20   31022287        31022287        -       A
  chr19   13054614        13054614        G       -
  </code></pre>
  
  <p><strong>Regular Expression</strong></p>
  
  <p>This uses enclosing groups to describe the important bits of the sequence. The groups are numbered from 1 upwards, with each number corresponding to a left bracket (in order), capturing the entirety of the sub-expression until the paired right bracket. I'll describe the groups, which are referenced in the subsequent part of the script as <code>$&lt;group_number&gt;</code>:</p>
  
  <ol>
  <li>Any number of characters, up to (but not including) the first ':'</li>
  <li>Any number of the characters '0' to '9' [i.e. numbers]</li>
  <li>Optionally, a "_", followed by any number of the characters '0' to '9'</li>
  <li>The numbers from Group <strong>[3]</strong>, i.e. excluding the "_"</li>
  <li>Optionally, a "dup" or a "del"</li>
  <li>Any number of characters selected from "A", "C", "G", "T" (i.e. bases)</li>
  <li>Optionally, an "ins" or ">", followed by any number of bases</li>
  <li>The "ins" or ">" from Group <strong>[7]</strong></li>
  <li>The bases from Group <strong>[7]</strong></li>
  </ol>
  
  <p><strong>Print Code</strong></p>
  
  <p>The <code>print</code> statement prints out a tab-separated list, finishing off with a line break. I'll describe each list element:</p>
  
  <ol>
  <li>Group <strong>[1]</strong></li>
  <li>Group <strong>[2]</strong></li>
  <li>If Group <strong>[4]</strong> exists, then Group <strong>[4]</strong>, otherwise Group <strong>[2]</strong></li>
  <li>If Group <strong>[8]</strong> is "ins", then Group <strong>[9]</strong>; [otherwise]<br>
  If Group <strong>[5]</strong> is "dup", then "-", otherwise Group <strong>[6]</strong></li>
  <li>If Group <strong>[5]</strong> is "dup", or group <strong>[8]</strong> is "ins", then Group <strong>[6]</strong>[otherwise]<br>
  If Group <strong>[9]</strong> exists, then Group <strong>[9]</strong>, otherwise "-"</li>
  </ol>
   96: <p>The following Python script seems to do the job.</p>
  
  <pre class="lang-py prettyprint-override"><code>#!/usr/bin/env python3
  
  from __future__ import print_function
  import re
  import sys
  
  for line in sys.stdin:
      if '_' in line:
          match = re.search(r'(\S+):(\d+)_(\d+)del([ACGT]+)ins([ACGT]+)', line)
          print(match.group(1), match.group(2), match.group(3), match.group(5), match.group(4), sep='\t')
      elif 'dup' in line or 'del' in line:
          match = re.search(r'(\S+):(\d+)(del|dup)([ACGT]+)', line)
          if match.group(3) == 'del':
              print(match.group(1), match.group(2), match.group(2), match.group(4), '-', sep='\t')
          else:
              print(match.group(1), match.group(2), match.group(2), '-', match.group(4), sep='\t')
      else:
          match = re.search(r'(\S+):(\d+)([ACGT]+)&gt;([ACGT]+)', line)
          coord = int(match.group(2))
          print(match.group(1), coord - 1, coord, match.group(3), match.group(4), sep='\t')
  </code></pre>
  
  <p>Invoked like so on the command line.</p>
  
  <pre><code>[standage@lappy ~] $ ./transform &lt; f1
  chr7 140453144 140453145 A T
  chr7 140453135 140453136 A T
  chr7    140453135   140453136   TT  CA
  chr20   31022287    31022287    -   A
  chr19   13054614    13054614    G   -
  </code></pre>
  