<commit_before>#!/usr/bin/env bash
if [ x$BASH = x ] || [ ! $BASH_VERSINFO ] || [ $BASH_VERSINFO -lt 4 ]; then
  echo "Error: Must use bash version 4+." >&2
  exit 1
fi
set -ue

MinReadsDefault=3
QualThresDefault=25
QualFormatDefault=sanger
Usage="Usage: \$ $(basename $0) [options] families.msa.tsv dcs1.fa dcs2.fa [sscs1.fa sscs2.fa]"

function main {

  # Read arguments.
  if [[ $# -lt 2 ]]; then
    fail "$Usage"
  fi
  min_reads=$MinReadsDefault
  qual_thres=$QualThresDefault
  qual_format=$QualFormatDefault
  while getopts ":r:q:F:h" opt; do
  case "$opt" in
      r) min_reads=$OPTARG;;
      q) qual_thres=$OPTARG;;
      F) qual_format=$OPTARG;;
      h) echo "$USAGE"
         exit;;
    esac
  done
  # Get positionals.
  alignments="${@:$OPTIND:1}"
  dcs1="${@:$OPTIND+1:1}"
  dcs2="${@:$OPTIND+2:1}"
  sscs1="${@:$OPTIND+3:1}"
  sscs2="${@:$OPTIND+4:1}"

  keep_sscs=
  if [[ $sscs1 ]] && [[ $sscs2 ]]; then
    keep_sscs=true
  elif [[ $sscs1 ]] || [[ $sscs2 ]]; then
    fail "Error: Must give both sscs1 and sscs2 or neither."
  fi

  # Find the actual directory this file resides in (resolving links).
  if readlink -f dummy >/dev/null 2>/dev/null; then
    script_path=$(readlink -f "${BASH_SOURCE[0]}")
  else
    script_path=$(perl -MCwd -le 'print Cwd::abs_path(shift)' "${BASH_SOURCE[0]}")
  fi
  script_dir=$(dirname "$script_path")

  sscs_args=
  if [[ $keep_sscs ]]; then
    sscs_args='--sscs-file sscs.fa'
  fi

  # Locate outconv.py.
  # In $script_dir/utils in a normal installation, $script_dir in a Conda installation.
  outconv_script="$script_dir/utils/outconv.py"
  if ! [[ -f "$outconv_script" ]]; then
    outconv_script="$script_dir/outconv.py"
  fi

  python2 "$script_dir/dunovo.py" -r $min_reads -q $qual_thres -F $qual_format "$alignments" \
    $sscs_args > duplex.fa
  python2 "$outconv_script" duplex.fa -1 "$dcs1" -2 "$dcs2"

  if [[ $keep_sscs ]]; then
    python2 "$outconv_script" sscs.fa -1 "$sscs1" -2 "$sscs2"
  fi
}

function fail {
  echo "$@" >&2
  exit 1
}
<commit_msg>dunovo.sh: Revamp argument parsing. - Allow long options. - Only need to enumerate whitelist of arguments to pass through once. - Add --galaxy and --phone-home.<commit_after>#!/usr/bin/env bash
if [ x$BASH = x ] || [ ! $BASH_VERSINFO ] || [ $BASH_VERSINFO -lt 4 ]; then
  echo "Error: Must use bash version 4+." >&2
  exit 1
fi
set -ue

Usage="Usage: \$ $(basename $0) [options] families.msa.tsv dcs1.fa dcs2.fa [sscs1.fa sscs2.fa]"

function main {

  # Parse options.
  # Only allow a whitelist of arguments to be passed through to dunovo.py.
  add_next=
  o=0
  opts=()
  p=0
  positionals=()
  for opt in "$@"; do
    add=
    if [[ $add_next ]]; then
      # The previous option took an argument.
      if [[ ${opt:0:1} == '-' ]]; then
        fail "Error: Option \"${opts[$((o-1))]}\" requires an argument (saw \"$opt\" instead)."
      else
        add=true
        add_next=
      fi
    else
      # nargs is how many "args" should be added for a particular option.
      # 1 means only that option itself.
      # 2 means the option and the following one (its argument).
      nargs=0
      case $opt in
        -h) fail "$Usage";;
        --galaxy) nargs=1;;
        --phone-home) nargs=1;;
        --test) nargs=1;;
        -r) nargs=2;;
        -q) nargs=2;;
        -F) nargs=2;;
      esac
      if [[ $nargs == 1 ]]; then
        add=true
      elif [[ $nargs == 2 ]]; then
        add=true
        add_next=true
      elif [[ ${opt:0:1} == '-' ]]; then
        fail "Error: Unrecognized option \"$opt\"."
      else
        # If it's an argument that doesn't start with "-" and it's not the argument of a previous
        # option, assume it's one of the positional arguments for this script.
        if [[ $p -gt 4 ]]; then
          fail "Error: Too many positional arguments. Failed on \"$opt\"."
        else
          positionals[$p]="$opt"
          p=$((p+1))
        fi
      fi
    fi
    if [[ $add ]]; then
      opts[$o]="$opt"
      o=$((o+1))
    fi
  done

  # Get positionals.
  if [[ ${#positionals[@]} -ge 3 ]]; then
    alignments="${positionals[0]}"
    dcs1="${positionals[1]}"
    dcs2="${positionals[2]}"
  else
    fail "Error: Too few positional arguments."
  fi
  if [[ ${#positionals[@]} -ge 5 ]]; then
    sscs1="${positionals[3]}"
    sscs2="${positionals[4]}"
    sscs_args='--sscs-file sscs.fa'
  else
    sscs1=
    sscs2=
    sscs_args=
  fi

  # Find the actual directory this file resides in (resolving links).
  if readlink -f dummy >/dev/null 2>/dev/null; then
    script_path=$(readlink -f "${BASH_SOURCE[0]}")
  else
    script_path=$(perl -MCwd -le 'print Cwd::abs_path(shift)' "${BASH_SOURCE[0]}")
  fi
  script_dir=$(dirname "$script_path")

  # Locate outconv.py.
  # In $script_dir/utils in a normal installation, $script_dir in a Conda installation.
  outconv_script="$script_dir/utils/outconv.py"
  if ! [[ -f "$outconv_script" ]]; then
    outconv_script="$script_dir/outconv.py"
  fi

  python2 "$script_dir/dunovo.py" "${opts[@]}" "$alignments" $sscs_args > duplex.fa
  python2 "$outconv_script" duplex.fa -1 "$dcs1" -2 "$dcs2"

  if [[ $sscs1 ]] && [[ $sscs2 ]]; then
    python2 "$outconv_script" sscs.fa -1 "$sscs1" -2 "$sscs2"
  fi
}

function fail {
  echo "$@" >&2
  exit 1
}

{'fraction_non_alphanumeric': 0.16562440780746637, 'fraction_numerical': 0.020655675573242372, 'mean_word_length': 2.7835125448028673, 'pattern_counts': {'":': 1, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7333431', 'n_tokens_mistral': 2050, 'n_tokens_neox': 1959, 'n_words': 530}