Skip to content

Commit 84e9105

Browse files
Add sinteractive session names and show CWD in --list
-n/--name tags a session via the SLURM Comment so it can be reattached by name (sinteractive --attach NAME). --list now resolves each session's current pane working directory by querying tmux over ssh in parallel, and shows it alongside the new NAME column. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ba61fdb commit 84e9105

1 file changed

Lines changed: 127 additions & 18 deletions

File tree

scripts/sinteractive

Lines changed: 127 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,28 @@ function parse_time {
6060
function usage {
6161
cat <<EOF
6262
Usage: sinteractive [OPTIONS] [-- SBATCH_ARGS...]
63-
sinteractive --attach JOBID
63+
sinteractive --attach JOBID|NAME
6464
6565
Start an interactive tmux session on a compute node via Slurm.
6666
6767
Options:
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+
7681
All 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+
312377
function 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

427511
function 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

Comments
 (0)