0

I'm trying to solve a very mundane problem. I want PS1 to change depending upon the previous command executed. Whether success or failure isn't the issue here. I want PS1 to include \w, but only if the last command entered was cd. What I have at the moment is:

if [[ !:0 == "cd" ]]
then
   PS1='(\w)[jobs: \j] > '
else
   PS1='[jobs: \j] > '

The output will always be the shorter one, regardless of the last command.

I feel like I'm making a simple mistake somewhere, and this also seems mundane enough that I can't find anything related through Google.

crit_d
  • 13
  • 4
  • So only `cd` and nothing else, no arguments? – Jetchisel Nov 09 '21 at 08:10
  • I would expect arguments, but I thought !:0 was specifically the command (and !:1 the first argument, and so on) – crit_d Nov 09 '21 at 08:23
  • Ah, your'e trying to use history expansion? – Jetchisel Nov 09 '21 at 08:25
  • I am, and unless there's a particular usage constraint on history expansion I'm not sure why it doesn't work. I admit, I've never tried to use it in this context before. – crit_d Nov 09 '21 at 08:27
  • I have disabled history expansion eons ago in my ~/.bashrc file, :-) – Jetchisel Nov 09 '21 at 08:28
  • Oh, maybe it's disabled by default? This is a new installation and I'd never thought about it. Why do you have them disabled? – crit_d Nov 09 '21 at 08:30
  • I don't do `!!` in an interactive session, but afaik history expansion is enable by default – Jetchisel Nov 09 '21 at 08:33
  • 2
    What about other commands (besides `cd`) which change the working directory? Perhaps what you need is to _detect a change in the working directory_, irrespective which command was causing it? – user1934428 Nov 09 '21 at 09:13
  • @Jetchisel Thanks to you I learned a new thing about .bashrc. : ) I appreciate the help. – crit_d Nov 09 '21 at 10:13
  • @user1934428 I think you're right. It started as a little throwaway QoL change, but now it's become an obsession, as these things tend to. – crit_d Nov 09 '21 at 10:14

3 Answers3

1

The Csh-style !:0 history expansion is an interactive feature. You can use the command history -p "!:0" to execute it in a script context, though (even when you have set +H, like most sane people have); but executing it inside PROMPT_COMMAND or the prompt itself is highly unwieldy. (When I tried, it would show me the penultimate command, or something from within the PROMPT_COMMAND scriptlet itself.)

Borrowing from https://stackoverflow.com/a/6110446/874188 (currently the accepted answer to Echoing the last command run in Bash?) I would go with

trap 'prompt_previous_command=$prompt_this_command; prompt_this_command=$BASH_COMMAND' DEBUG
PS1='$([[ ${prompt_previous_command%%\ *} == "cd" ]] && echo "(${PWD/$HOME/~})")[jobs: \j] \> '

It is unfortunate that echo "\\w" doesn't produce the expanded value in this context; ${PWD/$HOME/~} is a reasonable approximation, although there are corner cases where it gets it wrong.

... Perhaps a less confusing approach is to set the value in the trap already:

trap 'prompt_previous_command=$prompt_this_command
      prompt_this_command=$BASH_COMMAND
      [[ "${prompt_previous_command%%\ *}" == "cd" ]] &&
          prompt_cwd="(\\w)" || prompt_cwd=""
    PS1="$prompt_cwd[jobs: \\j] \\> "' DEBUG

Many Bash add-ons want to hook into your PROMPT_COMMAND and might sabotage any attempt to reserve it for youself; of course, this approach has a similar problem if you have something else in your system which relies on the DEBUG trap for something.

To make this work for pushd / popd and aliases etc too, here's an adaptation of Dan's excellent answer:

trap 'case ${prompt_prev_pwd-$PWD} in
       "$PWD") PS1="[jobs \\j] > ";;
       *) PS1="(\\w)[jobs: \\j] > ";;
      esac
      prompt_prev_pwd=$PWD' DEBUG
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I hadn't seen that Q/A before. There's a lot of good info there. I'm not too familiar with using bash debug beyond the basic troubleshooting stuff, so this was definitely helpful! Thank you! – crit_d Nov 09 '21 at 10:19
1

Put this in .bashrc:

PROMPT_COMMAND='
if [[ "$NEWPWD" != "$PWD" ]]; then
    PS1="(\w)[jobs: \j] > "
    NEWPWD=$PWD
else
    PS1="[jobs: \j] > "
fi'

You can use whichever name you want for $NEWPWD

It's simple, it works, and is not prone to errors.

dan
  • 4,846
  • 6
  • 15
  • I like that a lot, thank you! I kicked around the idea of a variable, but I dismissed it as too resource-intensive. I guess that really hasn't been an issue in decades. :-) This is really straightforward, thank you. – crit_d Nov 09 '21 at 10:17
  • It seemed like it was working, but for some reason it's now only ever outputting the long prompt, regardless of the state of $NEWPWD. `echo $NEWPWD` and `echo $PWD` have the same value, but the shorter prompt is still missing. – crit_d Nov 09 '21 at 10:34
  • It's working here. Probably ask a new question where you debug it with `set -x` if you can't figure it out. – tripleee Nov 09 '21 at 10:37
  • 1
    Probably [avoid upper case for your private variables](https://stackoverflow.com/questions/673055/correct-bash-and-shell-script-variable-capitalization) though. – tripleee Nov 09 '21 at 10:38
  • Fair points. Thank you, I'll see if I can figure it out. – crit_d Nov 09 '21 at 10:38
  • I'm speculating that this might very well be related to something else interfering with your `PROMPT_COMMAND` in interesting ways, as mentioned in my answer. – tripleee Nov 09 '21 at 10:50
  • Upvoted; added a variation of this in my answer, too – tripleee Nov 09 '21 at 10:51
  • SOLVED. It was a rookie mistake. I had a single-quote/double-quote conflict. For some reason I didn't get the usual syntax error, but that's a separate problem. Marked solved, thank you all! – crit_d Nov 09 '21 at 11:03
0

On approach is to create a function and parse history. The PROMPT_COMMAND is also needed.

Put the code below in your ~/.bashrc file or put it in another file, just make sure you source that file from ~/.bashrc

is_cd(){
  set -- $(history 1)
  if [[ $2 == "cd" ]]; then 
   echo cd_is_the_last_command
  else
    echo no_cd
  fi
}

if [[ $PROMPT_COMMAND != *is_cd* ]]; then
   PROMPT_COMMAND="is_cd"
fi

  • Change the lines with echo's with the actual command you want to execute.

  • Source ~/.bashrc after you have edited it.

  • This assumes that the output of your history has the numeric number first and command as the second column.

Jetchisel
  • 7,493
  • 2
  • 19
  • 18