Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I can type:

dirname ~/home/blah/file.zip

for instance, and this works fine alone, but when I use this syntax as the parameter for some command it always reads 'dirname' as the desired directory. i.e.:

cd dirname ~/home/blah/file.zip
bash: cd: dirname: No such file or directory

Basically, how do I get 'dirname file' to be read as one entity?

share|improve this question
    
Command substitution strips trailing newlines, which is a problem that quoting alone cannot solve. No sensible user or developer would put a newline at the end of any file name, but a malicious or clueless one might since Unix filesystems allow it. Parameter substitution is not vulnerable to this issue, and there are other reasons to prefer it to command substitution. The currently accepted answer uses command substitution, while @Gilles' answer which uses parameter expansion is the correct one. –  jw013 Aug 15 '12 at 18:22

4 Answers 4

up vote 5 down vote accepted
cd "$(dirname ~/home/blah/file.zip)"

$( is a form of command substitution. The BashGuide Wiki has some good information about this process.

share|improve this answer
    
See my comment on the question for why parameter substitution (e.g. @Gilles' answer) is better approach. It also uses one less external process. –  jw013 Aug 15 '12 at 18:25

(I won't repeat the command substitution feature already shown in other answers.)

If this comes up often, you can define a function. For example, put this in your ~/.bashrc:

d () {
  if ! [ -d "$1/." ]; then
    set -- "${1%/*}"
  fi
  cd -- "$1"
}

With this definition, you can run d /path/to/directory to change to the specified directory, or d /path/to/file to change to the directory containing the specified file (i.e. /path/to).

For this particular command, you can remove the last word by editing the command: with default key bindings, from the end of the line, press Alt+B to move back to the previous word and Alt+D to erase the word on the right. On most terminals, from the end of the line, you can press Esc Backspace to directly erase the word on the left.

If the path is a directory, ~/home/blah/subdir/.. designates the same directory as ~/home/blah.

In zsh, you can tack (:h) at the end of a path to strip off the file name and keep only the directory part. (It's h for “head”; t for “tail” retains only the file part.) You can use other history modifiers within (:…) after a path or glob.

cd ~/home/blah/file.zip(:h)
share|improve this answer

The "$(cmd)" form recommended by Chris is safest, but this is easier to type and will usually work just as well:

$ cd `dirname ~/home/blah/file.zip`

The difference is that the backticks form can be confused by certain characters in the command it's expanding. For a simple command like the one you show, you can immediately tell that it's not going to be a problem. For a shell script that needs to accept arbitrary arguments, it's safer to use the double-quoted $() form.

share|improve this answer
1  
There are two differences here: the presence of double quotes, and the backticks vs $(…). The double quotes protect special characters in the output of the command; it's often safe to leave them off on the command line (if you know you don't have any troublesome file names), but not in a script. The backticks have weird rules for quoting the command itself, but they're ok to use for a simple command with no tested ` or backtick; in scripts, you might as well forget they exist and stick to $(…)`. –  Gilles May 28 '12 at 23:36

(This is just a reply to @gilles answer as it is too big for a comment)

As Gilles already pointed out you can create a function which automatically perform the required function. But instead of using some random name you could also just redefine cd:

For zsh:

cd() {
    if (( ${#argv} == 1 )) && [[ -f ${1} ]]; then
        builtin cd ${1:h}
    else
        builtin cd "$@"
    fi
}

For bash:

cd() {
    if [ $# == 1 ] && [[ -f ${1} ]]; then
        builtin cd "$(dirname ${1})"
    else
        builtin cd "$@"
    fi
}

This will allow you to do something like cd /path/to/file and it will automatically change into the directory of the specific file. Of course you can use the bash version also with zsh.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.