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.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
TMH
  • 427
  • 6
  • 14

5 Answers5

17

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...)).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
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"
}
Rui F Ribeiro
  • 55,929
  • 26
  • 146
  • 227
UVV
  • 2,928
  • 3
  • 23
  • 36
  • 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 Nov 27 '14 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 Nov 27 '14 at 13:20
  • @TomHart it seems there's no `/bin/cd` in my CentOS, so this solution might not going to work – UVV Nov 27 '14 at 13:32
  • I've just noticed that now. I found this question which states why http://unix.stackexchange.com/questions/116955/where-is-cd-located. Still a good solution for system that actually have a /bin/cd though :). – TMH Nov 27 '14 at 13:33
  • @TomHart Actually I fixed the function using your link ;) – UVV Nov 27 '14 at 13:37
  • That's teamwork :P. I'll be able to get it working great now. Thanks :). – TMH Nov 27 '14 at 13:39
  • 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`. – Stéphane Chazelas Nov 27 '14 at 16:01
  • `.bash_profile` is for session settings, not for customising `bash` (for which there's `~/.bashrc`) – Stéphane Chazelas Nov 27 '14 at 16:04
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.

Simba
  • 1,632
  • 12
  • 14
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
    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)](http://unix.stackexchange.com/a/204928) for a safer approach. – Stéphane Chazelas Jan 14 '16 at 12:35
  • Thanks for the security insight @StéphaneChazelas. I have to admit I didn't even think about it. – lost-and-found Jan 29 '16 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. – DarkWiiPlayer Dec 23 '18 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
}
albfan
  • 231
  • 2
  • 6