I've created a small diff against tonight's bash master branch for displaying a (single!) prompt after reading a command but before executing said command.
It's kind of like trapping debug, but it doesn't output the prompt once for each subcommand in a pipeline - it's just once for the thing you typed. Even the following echo's just once: dstromberg@server:~/src/bash/src$ ./bash dstromberg@server:~/src/bash/src$ export PS0='fred> > ' dstromberg@server:~/src/bash/src$ pwd fred> /home/dstromberg/src/bash/src dstromberg@server:~/src/bash/src$ echo 1; echo 2; echo 3 fred> 1 2 3 dstromberg@server:~/src/bash/src$ My hope is that it can be merged into bash master. I didn't see a way of doing a pull request with savannah, and gmail was refusing to do an attachment, so I've shar'd the patch file and pasted it below.. Thanks for the cool software! #!/bin/sh # This is a shell archive (produced by GNU sharutils 4.11.1). # To extract the files from this archive, save it to some FILE, remove # everything before the `#!/bin/sh' line above, then type `sh FILE'. # lock_dir=_sh22239 # Made on 2015-10-24 18:51 PDT by <dstromberg@server>. # Source directory was `/tmp'. # # Existing files will *not* be overwritten, unless `-c' is specified. # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 2227 -rw-r--r-- ps0-diffs # MD5SUM=${MD5SUM-md5sum} f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'` test -n "${f}" && md5check=true || md5check=false ${md5check} || \ echo 'Note: not verifying md5sums. Consider installing GNU coreutils.' if test "X$1" = "X-c" then keep_file='' else keep_file=true fi echo=echo save_IFS="${IFS}" IFS="${IFS}:" gettext_dir= locale_dir= set_echo=false for dir in $PATH do if test -f $dir/gettext \ && ($dir/gettext --version >/dev/null 2>&1) then case `$dir/gettext --version 2>&1 | sed 1q` in *GNU*) gettext_dir=$dir set_echo=true break ;; esac fi done if ${set_echo} then set_echo=false for dir in $PATH do if test -f $dir/shar \ && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) then locale_dir=`$dir/shar --print-text-domain-dir` set_echo=true break fi done if ${set_echo} then TEXTDOMAINDIR=$locale_dir export TEXTDOMAINDIR TEXTDOMAIN=sharutils export TEXTDOMAIN echo="$gettext_dir/gettext -s" fi fi IFS="$save_IFS" if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null then if (echo -n test; echo 1,2,3) | grep n >/dev/null then shar_n= shar_c=' ' else shar_n=-n shar_c= ; fi else shar_n= shar_c='\c' ; fi f=shar-touch.$$ st1=200112312359.59 st2=123123592001.59 st2tr=123123592001.5 # old SysV 14-char limit st3=1231235901 if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \ test ! -f ${st1} && test -f ${f}; then shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"' elif touch -am ${st2} ${f} >/dev/null 2>&1 && \ test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"' elif touch -am ${st3} ${f} >/dev/null 2>&1 && \ test ! -f ${st3} && test -f ${f}; then shar_touch='touch -am $3$4$5$6$2 "$8"' else shar_touch=: echo ${echo} 'WARNING: not restoring timestamps. Consider getting and installing GNU `touch'\'', distributed in GNU coreutils...' echo fi rm -f ${st1} ${st2} ${st2tr} ${st3} ${f} # if test ! -d ${lock_dir} ; then : else ${echo} "lock directory ${lock_dir} exists" exit 1 fi if mkdir ${lock_dir} then ${echo} "x - created lock directory ${lock_dir}." else ${echo} "x - failed to create lock directory ${lock_dir}." exit 1 fi # ============= ps0-diffs ============== if test -n "${keep_file}" && test -f 'ps0-diffs' then ${echo} "x - SKIPPING ps0-diffs (file already exists)" else ${echo} "x - extracting ps0-diffs (text)" sed 's/^X//' << 'SHAR_EOF' > 'ps0-diffs' && diff --git a/doc/bash.1 b/doc/bash.1 index ec41462..97e46b3 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -2294,6 +2294,13 @@ trailing directory components to retain when expanding the \fB\ew\fP and X .B PROMPTING X below). Characters removed are replaced with an ellipsis. X .TP +.B PS0 +The value of this parameter is expanded (see +.SM +.B PROMPTING +below) and presented after a command is read, but before the command is +executed. The default value is ``'' (the null string). +.TP X .B PS1 X The value of this parameter is expanded (see X .SM diff --git a/eval.c b/eval.c index 1f65aac..9f103f0 100644 --- a/eval.c +++ b/eval.c @@ -53,6 +53,7 @@ extern int last_command_exit_value, stdin_redir; X extern int need_here_doc; X extern int current_command_number, current_command_line_count, line_number; X extern int expand_aliases; +extern char *ps0_prompt; X X #if defined (HAVE_POSIX_SIGNALS) X extern sigset_t top_level_mask; @@ -157,6 +158,20 @@ reader_loop () X X executing = 1; X stdin_redir = 0; + + /* After reading a command, before executing it */ + if (ps0_prompt) + { + char *ps0_string; + ps0_string = decode_prompt_string(ps0_prompt); + fprintf(stderr, ps0_string); + free(ps0_string); + /* stderr is often line buffered, so we flush; some + PS0 prompts will have a trailing newline, and + some won't. We flush for those that don't. */ + fflush(stderr); + } + X execute_command (current_command); X X exec_done: diff --git a/parse.y b/parse.y index 9cf7be8..90c3064 100644 --- a/parse.y +++ b/parse.y @@ -230,7 +230,7 @@ char *primary_prompt = PPROMPT; X char *secondary_prompt = SPROMPT; X X /* PROMPT_STRING_POINTER points to one of these, never to an actual string. */ -char *ps1_prompt, *ps2_prompt; +char *ps0_prompt, *ps1_prompt, *ps2_prompt; X X /* Handle on the current prompt string. Indirectly points through X ps1_ or ps2_prompt. */ @@ -5161,6 +5161,7 @@ prompt_again () X if (interactive == 0 || expanding_alias ()) /* XXX */ X return; X + ps0_prompt = get_string_value ("PS0"); X ps1_prompt = get_string_value ("PS1"); X ps2_prompt = get_string_value ("PS2"); X SHAR_EOF (set 20 15 10 24 18 38 07 'ps0-diffs' eval "${shar_touch}") && \ chmod 0644 'ps0-diffs' if test $? -ne 0 then ${echo} "restore of ps0-diffs failed" fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'ps0-diffs': 'MD5 check failed' ) << \SHAR_EOF bec797981104380abeb2463ff9dc7426 ps0-diffs SHAR_EOF else test `LC_ALL=C wc -c < 'ps0-diffs'` -ne 2227 && \ ${echo} "restoration warning: size of 'ps0-diffs' is not 2227" fi fi if rm -fr ${lock_dir} then ${echo} "x - removed lock directory ${lock_dir}." else ${echo} "x - failed to remove lock directory ${lock_dir}." exit 1 fi exit 0