Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH] Add ability to ignore running jobs when check_jobs is set



A bash convert on IRC wanted to know if there was a way to make the check_jobs
option work more like bash's default behaviour, which is to warn only if there
are stopped/suspended jobs (ignoring running ones). Refer to the description for
bash's own checkjobs option (which is disabled by default) here:

https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html

This is useful apparently for people who want to be warned if they've left their
editor or something running, but otherwise don't care about background jobs.

Someone also asked for this functionality in users/{6055,15353} (though they
were ultimately given a 'user-land' work-around that solved their particular
issue):

https://www.zsh.org/mla/users/2003/msg00351.html
https://www.zsh.org/mla/users/2010/msg00653.html

It seems like a useful option, and the work required was trivial, so i've
implemented it, along with some other minor things. Full change log:

- Add and document check_running_jobs option
- Fix minor formatting issue in documentation
- Clarify effect of long_list_jobs option
- Add tests for many interactive job-control features

The tests seem to work well and are much more comprehensive than what was there
before (i.e., almost nothing), but they feel a bit strange somehow. Let me know
if you have any thoughts as to how to make them less brittle or more idiomatic.

dana


diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index d043cf398..25b3d5736 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -817,7 +817,7 @@ zsh sessions will all have the new entries from their history lists
 added to the history file, in the order that they exit.
 The file will still be periodically re-written to trim it when the
 number of lines grows 20% beyond the value specified by
-tt($SAVEHIST) (see also the HIST_SAVE_BY_COPY option).
+tt($SAVEHIST) (see also the tt(HIST_SAVE_BY_COPY) option).
 )
 pindex(BANG_HIST)
 pindex(NO_BANG_HIST)
@@ -1429,6 +1429,19 @@ ifnzman(the section Special Functions in noderef(Functions))\
 ifzman(the section SPECIAL FUNCTIONS in zmanref(zshmisc))
 is not counted for this purpose.
 )
+pindex(CHECK_RUNNING_JOBS)
+pindex(NO_CHECK_RUNNING_JOBS)
+pindex(CHECKRUNNINGJOBS)
+pindex(NOCHECKRUNNINGJOBS)
+cindex(exiting, checking running jobs when)
+cindex(logging out, checking running jobs when)
+item(tt(CHECK_RUNNING_JOBS) <Z>)(
+Check for both running and suspended jobs when tt(CHECK_JOBS) is enabled.
+When this option is disabled, zsh checks only for suspended jobs, which
+matches the default behavior of bash.
+
+This option has no effect unless tt(CHECK_JOBS) is set.
+)
 pindex(HUP)
 pindex(NO_HUP)
 pindex(NOHUP)
@@ -1443,7 +1456,7 @@ pindex(LONGLISTJOBS)
 pindex(NOLONGLISTJOBS)
 cindex(jobs, list format)
 item(tt(LONG_LIST_JOBS) (tt(-R)))(
-List jobs in the long format by default.
+Print job notifications in the long format by default.
 )
 pindex(MONITOR)
 pindex(NO_MONITOR)
diff --git a/Src/builtin.c b/Src/builtin.c
index f002b9912..0211f2721 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5594,7 +5594,8 @@ checkjobs(void)
 
     for (i = 1; i <= maxjob; i++)
 	if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) &&
-	    !(jobtab[i].stat & STAT_NOPRINT))
+	    !(jobtab[i].stat & STAT_NOPRINT) &&
+	    (isset(CHECKRUNNINGJOBS) || jobtab[i].stat & STAT_STOPPED))
 	    break;
     if (i <= maxjob) {
 	if (jobtab[i].stat & STAT_STOPPED) {
diff --git a/Src/options.c b/Src/options.c
index 2b5795bab..590652ea9 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -111,6 +111,7 @@ static struct optname optns[] = {
 {{NULL, "chasedots",	      OPT_EMULATE},		 CHASEDOTS},
 {{NULL, "chaselinks",	      OPT_EMULATE},		 CHASELINKS},
 {{NULL, "checkjobs",	      OPT_EMULATE|OPT_ZSH},	 CHECKJOBS},
+{{NULL, "checkrunningjobs",   OPT_EMULATE|OPT_ZSH},	 CHECKRUNNINGJOBS},
 {{NULL, "clobber",	      OPT_EMULATE|OPT_ALL},	 CLOBBER},
 {{NULL, "combiningchars",     0},			 COMBININGCHARS},
 {{NULL, "completealiases",    0},			 COMPLETEALIASES},
diff --git a/Src/zsh.h b/Src/zsh.h
index 24d06ba06..29267b842 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2338,6 +2338,7 @@ enum {
     CHASEDOTS,
     CHASELINKS,
     CHECKJOBS,
+    CHECKRUNNINGJOBS,
     CLOBBER,
     APPENDCREATE,
     COMBININGCHARS,
diff --git a/Test/W02jobs.ztst b/Test/W02jobs.ztst
new file mode 100644
index 000000000..65b860072
--- /dev/null
+++ b/Test/W02jobs.ztst
@@ -0,0 +1,186 @@
+# Tests for interactive job control
+
+%prep
+
+  if [[ $OSTYPE == cygwin ]]; then
+    ZTST_unimplemented='the zsh/zpty module does not work on Cygwin'
+  elif zmodload zsh/zpty 2> /dev/null; then
+    zpty_start() {
+      export PS1= PS2=
+      zpty -d
+      zpty zsh "${(q)ZTST_testdir}/../Src/zsh -fiV +Z"
+    }
+    zpty_input() {
+      zpty -w zsh "${(F)@}" $'\n'
+    }
+    zpty_stop() {
+      # exit twice in case of check_jobs
+      zpty -w zsh $'exit\nexit\n'
+      # zpty gives no output when piped without these braces (?)
+      { zpty -r zsh } | sed $'/[^[:space:]]/!d; s/\r$//;'
+      zpty -d
+      :
+    }
+  else
+    ZTST_unimplemented='the zsh/zpty module is not available'
+  fi
+
+%test
+
+  zpty_start
+  zpty_input 'setopt no_long_list_jobs'
+  zpty_input ': &'
+  zpty_input 'wait'
+  zpty_stop
+0:job notification with no_long_list_jobs
+*>\[1] [0-9]##
+*>\[1]  + done[[:space:]]##:
+
+  zpty_start
+  zpty_input 'setopt long_list_jobs'
+  zpty_input ': &'
+  zpty_input 'wait'
+  zpty_stop
+0:job notification with long_list_jobs
+*>\[1] [0-9]##
+*>\[1]  + [0-9]## done[[:space:]]##:
+
+  zpty_start
+  zpty_input 'setopt no_hup no_check_jobs'
+  zpty_input 'sleep 3 &'
+  zpty_stop
+0:running job with no_hup + no_check_jobs
+*>\[1] [0-9]##
+
+  zpty_start
+  zpty_input 'setopt no_check_jobs'
+  zpty_input 'sleep 3 &'
+  zpty_stop
+0:running job with no_check_jobs
+*>\[1] [0-9]##
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'setopt check_jobs no_check_running_jobs'
+  zpty_input 'sleep 3 &'
+  zpty_stop
+0:running job with check_jobs + no_check_running_jobs
+*>\[1] [0-9]##
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'setopt check_jobs check_running_jobs'
+  zpty_input 'sleep 3 &'
+  zpty_stop
+0:running job with check_jobs + check_running_jobs
+*>\[1] [0-9]##
+*>zsh:*running jobs*
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'setopt check_jobs no_check_running_jobs'
+  zpty_input 'sleep 3'
+  sleep 0.1
+  zpty_input $'\C-z'
+  zpty_stop
+0:suspended job with check_jobs + no_check_running_jobs
+*>zsh:*(stopped|suspended)*sleep*
+*>zsh:*(stopped|suspended) jobs*
+# no 'SIGHUPed' message for suspended jobs
+
+  zpty_start
+  zpty_input 'setopt check_jobs check_running_jobs'
+  zpty_input 'sleep 3'
+  sleep 0.1
+  zpty_input $'\C-z'
+  zpty_stop
+0:suspended job with check_jobs + check_running_jobs
+*>zsh:*(stopped|suspended)*sleep*
+*>zsh:*(stopped|suspended) jobs*
+# no 'SIGHUPed' message for suspended jobs
+
+  zpty_start
+  zpty_input 'sleep 5 & sleep 4 & sleep 3 &'
+  zpty_input 'jobs'
+  zpty_stop
+0:`jobs` (misc.) with multiple running jobs
+*>\[1] [0-9]##
+*>\[2] [0-9]##
+*>\[3] [0-9]##
+*>\[1]    running*sleep 5*
+*>\[2]  - running*sleep 4*
+*>\[3]  + running*sleep 3*
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'sleep 3 &'
+  zpty_input 'jobs -l'
+  zpty_input 'jobs -p'
+  zpty_stop
+0:`jobs -l` and `jobs -p` with running job
+*>\[1] [0-9]##
+*>\[1]  + [0-9]## running*sleep*
+*>\[1]  + [0-9]## running*sleep*
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'sleep 3 &'
+  zpty_input 'jobs -d'
+  zpty_stop
+0:`jobs -d` with running job
+*>\[1] [0-9]##
+*>\[1]  + running*sleep*
+*>\(pwd : ?*\)
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'sleep 3 &'
+  zpty_input 'jobs -r'
+  zpty_input 'print -- -'
+  zpty_input 'jobs -s'
+  zpty_stop
+0:`jobs -r` and `jobs -s` with running job
+*>\[1] [0-9]##
+*>\[1]  + running*sleep*
+*>-
+*>zsh:*SIGHUPed*
+
+  zpty_start
+  zpty_input 'sleep 5'
+  sleep 0.1
+  zpty_input $'\C-z'
+  zpty_input 'jobs -r'
+  zpty_input 'print -- -'
+  zpty_input 'jobs -s'
+  zpty_stop
+0:`jobs -r` and `jobs -s` with suspended job
+*>zsh:*(stopped|suspended)*sleep*
+*>-
+*>\[1]  + (stopped|suspended)*sleep*
+# no 'SIGHUPed' message for suspended jobs
+
+  zpty_start
+  zpty_input 'sleep 10 & sleep 9 & sleep 8 & sleep 7 &'
+  sleep 0.1
+  zpty_input 'kill %4'
+  sleep 0.1
+  zpty_input 'kill -HUP %3'
+  sleep 0.1
+  zpty_input 'kill -INT %2'
+  sleep 0.1
+  zpty_input 'kill -KILL %1'
+  sleep 0.1
+  zpty_stop
+0:various `kill` signals with multiple running jobs
+*>\[1] [0-9]##
+*>\[2] [0-9]##
+*>\[3] [0-9]##
+*>\[4] [0-9]##
+*>\[4]  ? terminate*sleep*
+*>\[3]  ? hangup*sleep*
+*>\[2]  ? interrupt*sleep*
+*>\[1]  ? kill*sleep*
+
+%clean
+
+  zmodload -ui zsh/zpty




Messages sorted by: Reverse Date, Date, Thread, Author