This is my response to The Weekly Challenge #280.
$str, containing lowercase English letters only.
  
Input: $str = "acbddbca"
Output: "d"
Input: $str = "abccd"
Output: "c"
Input: $str = "abcdabbb"
Output: "a"
Brute force, i.e. a loop, is probably the best approach:
File: twice-appearance
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^<[a..z]>+$/);  # [1]
my %seen;                                          # [2]
for $str.comb -> $letter                           # [3]
{
  if %seen{$letter}                                # [4]
  {
    say $letter;                                   # [4a]
    last;                                          # [4b]
  }
  %seen{$letter} = True;                           # [5]
}
[1] Ensure that the string contains lowercase English letters only, one or more.
[2] Already encountered letters will be stored here.
[3] Iterate over each letter in the word.
[4] Have we seen the current letter before? If so, print it [4a] and exit [4b].
[5] Mark the current letter as seen.
Running it:
$ ./twice-appearance acbddbca
d
$ ./twice-appearance abccd
c
$ ./twice-appearance abcdabbb
a
Looking good.
No verbose mode, but here are some additional examples:
$ ./twice-appearance 12
Usage:
  ./twice-appearance [-v|--verbose[=Any]] <str>
$ ./twice-appearance abcdefg
  We can make it shorter, almost a one liner, with map instead of the explicit
  loop:
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^<[a..z]>+$/);
$str.comb.map({ state %seen; (say $_; last) if %seen{$_}++ });
  Note the state variable declared inside the map.
   See
  docs.raku.org/syntax/state
  for more information about the variable declarator state.
 See docs.raku.org/routine/map for more information about map.
Running this program gives the expected result:
$ ./twice-appearance-map acbddbca
d
$ ./twice-appearance-map abccd
c
$ ./twice-appearance-map abcdabbb
a
$str, where every two consecutive vertical bars are
  grouped into a pair.
  *, excluding any between
  each pair of vertical bars.
  
Input: $str = "p|*e*rl|w**e|*ekly|"
Ouput: 2
The characters we are looking here are "p" and "w**e".
Input: $str = "perl"
Ouput: 0
Input: $str = "th|ewe|e**|k|l***ych|alleng|e"
Ouput: 5
The characters we are looking here are "th", "e**", "l***ych" and "e".
If we split a string on, let us say a comma, we get the parts without the commas. This is indeed what happens on parsing a CSV (comma separated values) file.
An example:
> "This,does,not,make,sense".split(",").raku
("This", "does", "not", "make", "sense").Seq
We can split on regular expressions instead of single characters (or strings).
> "This,does not;make,any  sense".split(/<[\ ,;.]>+/).raku
("This", "does", "not", "make", "any", "sense").Seq
So it should not come as a big surprise that we can use a pattern matching the pairs of vertical bars, and split on that.
File: count-asterisks
#! /usr/bin/env raku
unit sub MAIN ($str, :v(:$verbose));             # [1]
my @parts = $str.split(/\|.*?\|/);               # [2]
say ": Parts: { @parts.raku }" if $verbose;
@parts.join.comb.grep( * eq '*').elems.say;      # [3]
[1] No restrictions on the string this time.
[2] Split on pairs of vertical bars (with some non-«vertical bars» between them).
[3] Join the resulting array, split it into individual characters, get rid of non-asterisks, and count the result (i.e. the asterisks).
Running it:
$ ./count-asterisks "p|*e*rl|w**e|*ekly|"
2
$ ./count-asterisks "perl"
0
$ ./count-asterisks "th|ewe|e**|k|l***ych|alleng|e"
5
Looking good.
With verbose mode:
$ ./count-asterisks -v "p|*e*rl|w**e|*ekly|"
: Parts: ["p", "w**e", ""]
2
$ ./count-asterisks -v "perl"
: Parts: ["perl"]
0
$ ./count-asterisks -v "th|ewe|e**|k|l***ych|alleng|e"
: Parts: ["th", "e**", "l***ych", "e"]
5
And that's it.