13

I'm trying to set something up on a server I run, when ever I cd into a public_html folder 95% of the time there's a few commands I will always run to check certain things.

Is there anyway I can hook into cd so if the directory is a public_html, it will automatically run the commands for me?

If I can't hook into the cd command, are there any other things I could do to achieve the outcome I'm after?

I'm running CentOS 5.8.

5 Answers 5

18

With ksh or bash (or zsh):

cd() {
  builtin cd "$@" || return
  [ "$OLDPWD" = "$PWD" ] || case $PWD in
      (*/public_html) echo do something
    esac
}

With zsh:

chpwd()
  case $PWD in
    (*/public_html) echo do something
  esac

(chpwd is a hook function that is called whenever the current working directory changes (by way of cd, pushd, popd...)).

15

You could add this function to your .bashrc or other startup file (depending on your shell).

cd() {      
   if [ "$1" = "public_html" ]; then
      echo "current dir is my dir"
   fi
   builtin cd "$1"
}
8
  • Ooh, this looks interesting. I've added this to my .bash_profile, and added a echo "Testing..." just above the if, but nothings output. Do I need do something to apply these changes?
    – TMH
    Commented Nov 27, 2014 at 13:19
  • @TomHart If you didn't start a new session then you have to source the file into your current session by . ~/.bash_profile
    – UVV
    Commented Nov 27, 2014 at 13:20
  • @TomHart it seems there's no /bin/cd in my CentOS, so this solution might not going to work
    – UVV
    Commented Nov 27, 2014 at 13:32
  • I've just noticed that now. I found this question which states why unix.stackexchange.com/questions/116955/where-is-cd-located. Still a good solution for system that actually have a /bin/cd though :).
    – TMH
    Commented Nov 27, 2014 at 13:33
  • 1
    Does not work if you cd -P public_html or cd ~/public_html or has unexpected behaviour if you can't cd into public_html. Commented Nov 27, 2014 at 16:01
9

Wrapping cd, an existing command, is not recommended.

A more universal solution would be defining a custom chpwd hook in Bash. (According to tags of this question, I assume you're using Bash)

There's not a complete hook system designed in Bash when compared with other modern shells. PROMPT_COMMAND variable is used as a hook function, which is equivalent to precmd hook in ZSH, fish_prompt in Fish. For the time being, ZSH is the only shell I've known that has a chpwd hook builtin.

PROMPT_COMMAND

If set, the value is interpreted as a command to execute before the printing of each primary prompt ($PS1).

https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Bash-Variables

chpwd Hook in Bash

A trick is provided to setup a chpwd equivalent hook in Bash based on PROMPT_COMMAND.

# create a PROPMT_COMMAND equivalent to store chpwd functions
typeset -g CHPWD_COMMAND=""

_chpwd_hook() {
  shopt -s nullglob

  local f

  # run commands in CHPWD_COMMAND variable on dir change
  if [[ "$PREVPWD" != "$PWD" ]]; then
    local IFS=$';'
    for f in $CHPWD_COMMAND; do
      "$f"
    done
    unset IFS
  fi
  # refresh last working dir record
  export PREVPWD="$PWD"
}

# add `;` after _chpwd_hook if PROMPT_COMMAND is not empty
PROMPT_COMMAND="_chpwd_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"

Since we're detecting PWD change directly, the solution works with cd, pushd, and popd.

Note: The main difference between our chpwd implementaion in Bash and the chpwd in ZSH is, PROMPT_COMMAND is not supported in a non-interactive Bash shell.

Usage

_public_html_action() {
  if [[ $PWD == */public_html ]]; then
    # actions
  fi
}

# append the command into CHPWD_COMMAND
CHPWD_COMMAND="${CHPWD_COMMAND:+$CHPWD_COMMAND;}_public_html_action"

Source: Create chpwd Equivalent Hook in Bash from my gist.

For anyone want an answer for ZSH. Use chpwd hook in ZSH. Don't define chpwd() function directly. More detail here.

0
1

I am not a bash expert but I would take @UVV's answer and modify it just a little so that it does this:

  • instead of checking for public_html, I'd just check for some hook-script file in the target dir $1, say, cd_hook.sh.
  • if that hook-script exists, run it, and move on with the cd

This seems more generic because you'd then be able to apply the cd-hook to any directory if you so choose, by just adding a cd_hook.sh in the said directory.

3
  • 3
    It seems generic, but you're introducing a security vulnerability. Someone or something only needs to plant a hook-script in /tmp for instance to make you run any code the next time you cd there. You'd want at least to make sure that the hook-script is owned by you, not a symlink, not writable by anyone other than you, that the directory it's in is only writable by you... See Keeping history per working directory (cf. per shell session) for a safer approach. Commented Jan 14, 2016 at 12:35
  • Thanks for the security insight @StéphaneChazelas. I have to admit I didn't even think about it. Commented Jan 29, 2016 at 12:42
  • I found this question while doing just that: if I cd somewhere, check for a .bashcd file and run it if it exists. Commented Dec 23, 2018 at 20:36
0

Using powerful zsh approach in bash:

First an easy way to extend bash:

~/.runscripts

#load all scripts inside $1 dir

run_scripts()
{
    for script in $1/*; do

        # skip non-executable snippets
        [ -f "$script" ] && [ -x "$script" ] || continue

        # execute $script in the context of the current shell
        . $script
    done
}

Include in to .bashrc:

. ~/.run_scripts

run_scripts ~/.bashrc.d

You can create ~/.bashrc.d/cdhook with:

#!/bin/bash

chpwd() {
  : #no action
}

cd() {      
   builtin cd $1
   chpwd $1
}

Now is up to you to replace the function:

#list files after cd
chpwd() {
  ls -lah --color
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .