@@ -60,22 +60,28 @@ function parse_time {
6060function usage {
6161 cat << EOF
6262Usage: sinteractive [OPTIONS] [-- SBATCH_ARGS...]
63- sinteractive --attach JOBID
63+ sinteractive --attach JOBID|NAME
6464
6565Start an interactive tmux session on a compute node via Slurm.
6666
6767Options:
68+ -n, --name NAME Tag the session with a name for easy reattach
69+ (allowed: letters, digits, '.', '_', '-')
6870 --node NODE Request a specific compute node (e.g., compute01)
6971 --partition PART Slurm partition (default: interactive)
7072 --time TIME Wall time limit (default: 1 day)
7173 Supports shorthand: 8h, 30m, 2d, 1d12h, 1h30m
72- --attach JOBID Reattach to a running sinteractive session
73- --list List running sinteractive sessions
74+ --attach TARGET Reattach to a running session by JOBID or NAME
75+ --list List running sinteractive sessions (with NAME and CWD)
7476 -h, --help Show this help message
7577
78+ Note: -n is consumed by sinteractive as --name. Use --ntasks=N to set
79+ sbatch ntasks instead of -n.
80+
7681All other options are passed directly to sbatch. Examples:
7782
7883 sinteractive # 8h session, 2 CPUs, 8G memory
84+ sinteractive -n myproj # named session
7985 sinteractive --node compute01 # run on a specific node
8086 sinteractive --time=2h -p rna # 2h session on rna partition
8187 sinteractive --mem=16G --cpus-per-task=4
@@ -85,7 +91,8 @@ Reconnecting after a disconnect:
8591 If your SSH connection drops or you detach (Ctrl-b d), the tmux
8692 session keeps running. To reconnect from the login node:
8793
88- sinteractive --attach JOBID # reattach to the session
94+ sinteractive --attach JOBID # reattach by job id
95+ sinteractive --attach myproj # reattach by name
8996
9097 Use "sinteractive --list" to find your running sessions.
9198
@@ -116,7 +123,8 @@ function main {
116123
117124 # Parse custom flags before passing remaining args to sbatch
118125 local args=()
119- local reattach_jobid=' '
126+ local reattach_target=' '
127+ local session_name=' '
120128 while [[ $# -gt 0 ]]; do
121129 case " $1 " in
122130 -h | --help)
@@ -126,11 +134,19 @@ function main {
126134 list_sessions
127135 ;;
128136 --attach)
129- reattach_jobid =" $2 "
137+ reattach_target =" $2 "
130138 shift 2
131139 ;;
132140 --attach=* )
133- reattach_jobid=" ${1# --attach=} "
141+ reattach_target=" ${1# --attach=} "
142+ shift
143+ ;;
144+ -n | --name)
145+ session_name=" $2 "
146+ shift 2
147+ ;;
148+ --name=* )
149+ session_name=" ${1# --name=} "
134150 shift
135151 ;;
136152 --node)
@@ -168,12 +184,35 @@ function main {
168184 esac
169185 done
170186
171- if [[ -n " ${reattach_jobid} " ]]; then
172- reattach " ${reattach_jobid} "
187+ if [[ -n " ${reattach_target} " ]]; then
188+ reattach " ${reattach_target} "
189+ fi
190+
191+ if [[ -n " ${session_name} " ]]; then
192+ if ! [[ " ${session_name} " =~ ^[A-Za-z0-9._-]+$ ]]; then
193+ echo 1>&2 " Error: --name must contain only letters, digits, '.', '_', '-'"
194+ exit 1
195+ fi
196+ local existing
197+ existing=$( squeue --me --states RUNNING,PENDING --noheader \
198+ --Format=JobID:12,Comment:80 2> /dev/null |
199+ awk -v n=" sinteractive:${session_name} " ' $2 == n {print $1}' )
200+ if [[ -n " ${existing} " ]]; then
201+ echo 1>&2 " Error: A sinteractive session named '${session_name} ' is already running:"
202+ echo 1>&2 " JobID(s): $( echo ${existing} | xargs) "
203+ echo 1>&2 " Pick a different name, or reattach with: sinteractive --attach ${session_name} "
204+ exit 1
205+ fi
173206 fi
174207
175208 set -- " ${args[@]} "
176209
210+ # If a session name was given and the user did not pass --job-name/-J,
211+ # set it so the name shows up natively in squeue output.
212+ if [[ -n " ${session_name} " ]] && ! printf ' %s\n' " $@ " | grep -qE ' ^(--job-name|-J)' ; then
213+ set -- --job-name=" sint-${session_name} " " $@ "
214+ fi
215+
177216 # Default to 8 hours if no --time/-t specified
178217 if ! printf ' %s\n' " $@ " | grep -qE ' ^(--time|-t)' ; then
179218 set -- --time=24:00:00 " $@ "
@@ -218,7 +257,11 @@ function main {
218257 exit " $sbatch_ec "
219258 fi
220259
221- scontrol update JobId=" ${JOB_ID} " Comment=" sinteractive-${JOB_ID} "
260+ local job_comment=" sinteractive"
261+ if [[ -n " ${session_name} " ]]; then
262+ job_comment=" sinteractive:${session_name} "
263+ fi
264+ scontrol update JobId=" ${JOB_ID} " Comment=" ${job_comment} "
222265
223266 echo 1>&2 " Interactive job with ID ${JOB_ID} submitted, please wait."
224267 local state
@@ -264,8 +307,12 @@ function main {
264307 echo 1>&2 " Disconnected from sinteractive session."
265308 echo 1>&2 " Your job ${JOB_ID} is still running on ${batchhost} ."
266309 echo 1>&2 ' '
310+ local attach_target=" ${JOB_ID} "
311+ if [[ -n " ${session_name} " ]]; then
312+ attach_target=" ${session_name} "
313+ fi
267314 echo 1>&2 " To reconnect:"
268- echo 1>&2 " sinteractive --attach ${JOB_ID } "
315+ echo 1>&2 " sinteractive --attach ${attach_target } "
269316 echo 1>&2 ' '
270317 echo 1>&2 " To cancel the job:"
271318 echo 1>&2 " scancel ${JOB_ID} "
@@ -309,24 +356,61 @@ function main {
309356 fi
310357}
311358
359+ # Query the current working directory of an sinteractive tmux session by
360+ # asking tmux on the compute node. Returns empty string on failure.
361+ function get_session_cwd {
362+ local node=$1 jobid=$2
363+ local sock=" sinteractive-${jobid} "
364+ local session=" sinteractive-${jobid} "
365+ local cwd
366+ cwd=$( ssh -o BatchMode=yes -o ConnectTimeout=3 -o StrictHostKeyChecking=accept-new \
367+ " $node " \
368+ " $TMUX_BIN -L '$sock ' display-message -p -t '$session ' '#{pane_current_path}'" \
369+ 2> /dev/null)
370+ # Collapse $HOME prefix to ~ for readability
371+ if [[ -n " $cwd " && " $cwd " == " $HOME " * ]]; then
372+ cwd=" ~${cwd# $HOME } "
373+ fi
374+ printf ' %s' " $cwd "
375+ }
376+
312377function list_sessions {
313378 local lines
314379 lines=$( squeue --me --states RUNNING --noheader \
315- --Format=JobID:12,Comment:30 ,NodeList:16,TimeUsed:12,TimeLimit:12 |
316- grep sinteractive)
380+ --Format=JobID:12,Comment:80 ,NodeList:16,TimeUsed:12,TimeLimit:12 |
381+ grep -E ' [[:space:]] sinteractive([:-]|[[:space:]]|$) ' )
317382
318383 if [[ -z " $lines " ]]; then
319384 echo " No running sinteractive sessions."
320385 exit 0
321386 fi
322387
323- printf ' %-10s %-14s %-10s %-10s\n' ' JOBID' ' NODE' ' ELAPSED' ' TIMELIMIT'
388+ # Query cwd for each session in parallel; results land in $tmpdir/<jobid>
389+ local tmpdir
390+ tmpdir=$( mktemp -d)
391+ while read -r jobid comment node elapsed limit; do
392+ (get_session_cwd " $node " " $jobid " > " $tmpdir /$jobid " ) &
393+ done <<< " $lines"
394+ wait
395+
396+ printf ' %-10s %-20s %-14s %-10s %-10s %s\n' \
397+ ' JOBID' ' NAME' ' NODE' ' ELAPSED' ' TIMELIMIT' ' CWD'
324398 while read -r jobid comment node elapsed limit; do
325- printf ' %-10s %-14s %-10s %-10s\n' " $jobid " " $node " " $elapsed " " $limit "
399+ local name=' -'
400+ if [[ " $comment " =~ ^sinteractive:(.+)$ ]]; then
401+ name=" ${BASH_REMATCH[1]} "
402+ fi
403+ local cwd
404+ cwd=$( < " $tmpdir /$jobid " )
405+ [[ -z " $cwd " ]] && cwd=' -'
406+ printf ' %-10s %-20s %-14s %-10s %-10s %s\n' \
407+ " $jobid " " $name " " $node " " $elapsed " " $limit " " $cwd "
326408 done <<< " $lines"
327409
410+ rm -rf " $tmpdir "
411+
328412 echo ' '
329- echo ' To reattach: sinteractive --attach JOBID'
413+ echo ' To reattach: sinteractive --attach JOBID (or NAME if set) '
330414 exit 0
331415}
332416
@@ -425,12 +509,37 @@ function check_job_limit {
425509}
426510
427511function reattach {
428- local jobid=$1
512+ local target=$1
513+ local jobid
514+
515+ if [[ " ${target} " =~ ^[0-9]+$ ]]; then
516+ jobid=" ${target} "
517+ else
518+ # Resolve by name via the Comment marker set at submit time
519+ local matches
520+ matches=$( squeue --me --states RUNNING --noheader \
521+ --Format=JobID:12,Comment:80 2> /dev/null |
522+ awk -v n=" sinteractive:${target} " ' $2 == n {print $1}' )
523+ local count
524+ count=$( printf ' %s\n' " ${matches} " | grep -c .)
525+ if [[ " ${count} " -eq 0 ]]; then
526+ echo 1>&2 " $0 : no running sinteractive session named '${target} '"
527+ echo 1>&2 " Run 'sinteractive --list' to see available sessions."
528+ exit 1
529+ elif [[ " ${count} " -gt 1 ]]; then
530+ echo 1>&2 " $0 : multiple running sessions named '${target} ':"
531+ echo 1>&2 " $( echo ${matches} | xargs) "
532+ echo 1>&2 " Reattach by JOBID instead."
533+ exit 1
534+ fi
535+ jobid=" ${matches} "
536+ fi
537+
429538 local sock=" sinteractive-${jobid} "
430539
431540 # Verify the job is still running
432541 local state
433- state=$( squeue --jobs " ${jobid} " --states RUNNING --noheader --Format state | xargs)
542+ state=$( squeue --jobs " ${jobid} " --states RUNNING --noheader --Format state 2> /dev/null | xargs)
434543 if [[ " ${state} " != ' RUNNING' ]]; then
435544 echo 1>&2 " $0 : job ${jobid} is not running (state: ${state:- unknown} )"
436545 exit 1
0 commit comments