d068f3195e28f60403917212dc139504b392e7c6
max
  Tue Sep 15 04:56:49 2026 -0700
uniprot otto: guard the shift in doUpdate.sh

shift is a POSIX special built-in, so a shell that follows that rule exits when it fails.
Under dash, which is /bin/sh on Debian and in most containers, running doUpdate.sh with
no arguments ended the script at that line and doUniprot never started. Redirecting the
complaint to /dev/null hid the reason, so the run log showed a START with no END, which
reads exactly like a run still in progress. /bin/sh is bash on hgwdev, where it works.

diff --git src/hg/utils/otto/uniprot/doUpdate.sh src/hg/utils/otto/uniprot/doUpdate.sh
index be9ac5e6dcf..d2a50a882d5 100755
--- src/hg/utils/otto/uniprot/doUpdate.sh
+++ src/hg/utils/otto/uniprot/doUpdate.sh
@@ -31,31 +31,41 @@
     echo "UniProt update did not start: ./uniprotToTab cannot be run."
     echo
     echo "The lxml python module does not import. Rebuild the environment with:"
     echo "    cd /hive/data/outside/otto/uniprot && ./makeVenv.sh"
     echo
     ./uniprotToTab --help 2>&1 | tail -20
     exit 1
 fi
 
 # A killed run would otherwise leave a START with no matching line, which reads the same
 # as a run that is still going. Say it was interrupted, and drop the lock file, which
 # doUniprot's own atexit handler does not get to run on a signal.
 trap 'logRun "INTERRUPTED killed by a signal"; rm -f /hive/data/outside/uniProt/current/doUniprot.lock; exit 130' INT TERM HUP
 
 logRun "START"
-./doUniprot run > lastRun.log 2>&1
+# Anything after the first argument is handed to doUniprot, so a hand restart can say
+# "./doUpdate.sh run -p" to skip the download and the multi-day parse and still get the
+# lock handling, the run log and the failure mail. Cron passes only "run".
+# Guard the shift rather than hiding its complaint: shift is a POSIX special built-in, so
+# in a shell that follows that rule (dash, which is /bin/sh on Debian and in most
+# containers) shifting an empty argument list ends the script then and there. The run log
+# would show a START with no END, which reads exactly like a run still in progress.
+if [ $# -gt 0 ]; then
+    shift
+fi
+./doUniprot run "$@" > lastRun.log 2>&1
 exitCode=$?
 trap - INT TERM HUP
 logRun "END exit=$exitCode"
 
 if grep -q "Is a doUniprot process already running" lastRun.log ; then
     # A run from last month, or a hand-started one, is still going, or crashed and left
     # its lock file behind. Say so in one line instead of the failure report below: this
     # is not a broken pipeline, but a stale lock does need someone to look at it.
     logRun "LOCKED another doUniprot run holds the lock file"
     echo "UniProt update skipped: another doUniprot run holds the lock file."
     echo "If nothing is running, remove /hive/data/outside/uniProt/current/doUniprot.lock"
     exit 0
 fi
 
 if [ $exitCode -ne 0 ] ; then