1191

How could I retrieve the current working directory/folder name in a bash script, or even better, just a terminal command.

pwd gives the full path of the current working directory, e.g. /opt/local/bin but I only want bin.

1

24 Answers 24

1538

No need for basename, and especially no need for a subshell running pwd (which adds an extra, and expensive, fork operation); the shell can do this internally using parameter expansion:

result=${PWD##*/}          # to assign to a variable
result=${result:-/}        # to correct for the case where PWD is / (root)

printf '%s\n' "${PWD##*/}" # to print to stdout
                           # ...more robust than echo for unusual names
                           #    (consider a directory named -e or -n)

printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
                           # ...useful to make hidden characters readable.

Note that if you're applying this technique in other circumstances (not PWD, but some other variable holding a directory name), you might need to trim any trailing slashes. The below uses bash's extglob support to work even with multiple trailing slashes:

dirname=/path/to/somewhere//
shopt -s extglob           # enable +(...) glob syntax
result=${dirname%%+(/)}    # trim however many trailing slashes exist
result=${result##*/}       # remove everything before the last / that still remains
result=${result:-/}        # correct for dirname=/ case
printf '%s\n' "$result"

Alternatively, without extglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /
result=${result:-/}                     # correct for dirname=/ case
Sign up to request clarification or add additional context in comments.

32 Comments

What is the difference between ${PWD##*/} and $PWD?
@Mr_Chimp the former is a parameter expansion operations which trims the rest of the directory to provide only the basename. I have a link in my answer to the documentation on parameter expansion; feel free to follow that if you have any questions.
@stefgosselin $PWD is the name of a built-in bash variable; all built-in variable names are in all-caps (to distinguish them from local variables, which should always have at least one lower-case character). result=${PWD#*/} does not evaluate to /full/path/to/directory; instead, it strips only the first element, making it path/to/directory; using two # characters makes the pattern match greedy, matching as many characters as it can. Read the parameter expansion page, linked in the answer, for more.
Note that the output from pwd is not always the same as the value of $PWD, due to complications arising from cding through symbolic links, whether bash has the -o physical option set, and so on. This used to get especially nasty around handling of automounted directories, where recording the physical path instead of the logical one would produce a path that, if used, would allow the automounter to spontaneously dismount the directory one was using.
Nice! Someone should write a book for old Borne shell guys like me. Maybe there is one out there? It could have a crotchity old sys admin saying stuff like, "back in my day we only sh and csh and if you wanted the backspace key to work you had to read the whole stty man page, and we liked it!"
|
590

Use the basename program. For your case:

% basename "$PWD"
bin

14 Comments

Isn't this the purpose of the basename program? What is wrong with this answer besides missing the quotes?
@Nacht basename is indeed an external program that does the right thing, but running any external program for a thing bash can do out-of-the-box using only built-in functionality is silly, incurring performance impact (fork(), execve(), wait(), etc) for no reason.
...as an aside, my objections to basename here don't apply to dirname, because dirname has functionality that ${PWD##*/} does not -- transforming a string with no slashes to ., for instance. Thus, while using dirname as an external tool has performance overhead, it also has functionality that helps to compensate for same.
Sure using basename is less "efficient". But it's probably more efficient in terms of developer productivity because it's easy to remember compared to the ugly bash syntax. So if you remember it, go for it. Otherwise, basename works just as well. Has anyone ever had to improve the "performance" of a bash script and make it more efficient?
@usr, ...speaking as someone who's written bash scripts that operate over maildirs (a mailbox storage format with one file per email, as used by qmail), yes, absolutely, I've had real-world cause to pay attention to efficiency in scripting. We're talking on the scale of 20ms per command substitution on non-embedded hardware -- loop over 100,000 items and you're talking real time even if your code just has one of them. (Sure, there's a point where shell is no longer the right tool, but you reach that point far faster if you don't pay any attention to how well you write your code).
|
182
$ echo "${PWD##*/}"

​​​​​

2 Comments

@jocap ...except that the usage of echo there, without quoting the argument, is wrong. If the directory name has multiple spaces, or a tab character, it'll be collapsed to a single space; if it has a wildcard character, it will be expanded. Correct usage would be echo "${PWD##*/}".
@jocap ...also, I'm not sure that "echo" is in fact a legitimate part of the answer. The question was how to get the answer, not how to print the answer; if the goal was to assign it to a variable, for instance, name=${PWD##*/} would be right, and name=$(echo "${PWD##*/}") would be wrong (in a needless-inefficiency sense).
38

You can use a combination of pwd and basename. E.g.

#!/bin/bash

CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`

echo "$BASENAME"

exit;

4 Comments

Please, no. The backticks create a subshell (thus, a fork operation) -- and in this case, you're using them twice! [As an additional, albeit stylistic quibble -- variable names should be lower-case unless you're exporting them to the environment or they're a special, reserved case].
and as a second style quibble backtics should be replaced by $(). still forks but more readable and with less excaping needed.
By convention, environment variables (PATH, EDITOR, SHELL, ...) and internal shell variables (BASH_VERSION, RANDOM, ...) are fully capitalized. All other variable names should be lowercase. Since variable names are case-sensitive, this convention avoids accidentally overriding environmental and internal variables.
Depends on the convention. One could argue that all shell variables are environment variables (depending on the shell), and thus all shell variables should be in all-caps. Someone else could argue based on scope - global variables are all-caps, while local variables are lowercase, and these are all globals. Yet another might argue that making variables all-uppercase adds an extra layer of contrast with the commands littering shell scripts, which are also all lower-case short but meaningful words. I may or may not /agree/ with any of those arguments, but I've seen all three in use. :)
35

Use:

basename "$PWD"

OR

IFS=/ 
var=($PWD)
echo ${var[-1]} 

Turn the Internal Filename Separator (IFS) back to space.

IFS= 

There is one space after the IFS.

4 Comments

Reads well, less magic!
This is the same as Arton Dorneles' 2016 answer isn't it? IFS to slash, $PWD to array, read last array element? Minus some IFS restoration, and failing if a path contains *, ?, or [ characters
@BretWeinraub, I disagree -- there's lots of magic here, it's just largely invisible (for example, unquoted expansions run string-splitting -- which is desired in the first case, undesired in the second one -- and globbing -- which is desired in neither case, and can cause wildly unexpected/surprising results if the directory name contains glob characters and either nullglob or failglob options are enabled). I'd argue that invisible magic is the worst kind: because people don't know it's there, they don't know what the gotchas and corner cases are.
...if you see some syntax you don't know, you can look it up and understand what it does. When you see syntax that looks obvious but has hidden meanings that only experts understand, you don't know there's anything to look for until you step on one of the covered pitfalls.
21

How about grep:

pwd | grep -o '[^/]*$'

1 Comment

would not recommend because it seems to get some color information whereas pwd | xargs basename doesn't.. probably not that important but the other answer is simpler and more consistent across environments
14

This thread is great! Here is one more flavor:

pwd | awk -F / '{print $NF}'

Comments

14
basename $(pwd)

or

echo "$(basename $(pwd))"

3 Comments

Needs more quotes to be correct -- as it is, the output of pwd is string-split and glob-expanded before being passed to basename.
Thus, basename "$(pwd)" -- though that's very inefficient compared to just basename "$PWD", which is itself inefficient compared to using a parameter expansion instead of calling basename at all.
love this because I will simply never remember echo "${PWD##*/}" haha 😂 .
12

Just run the following command line:

basename $(pwd)

If you want to copy that name:

basename $(pwd) | pbcopy

Or using xclip command if it is installed:

basename $(pwd) | xclip -selection clipboard

enter image description here

1 Comment

nice and clean one! though in order to handle whitespaces in path components it need quotes like basename "$(pwd)"
11

I like the selected answer (Charles Duffy), but be careful if you are in a symlinked dir and you want the name of the target dir. Unfortunately I don't think it can be done in a single parameter expansion expression, perhaps I'm mistaken. This should work:

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}

To see this, an experiment:

cd foo
ln -s . bar
echo ${PWD##*/}

reports "bar"

DIRNAME

To show the leading directories of a path (without incurring a fork-exec of /usr/bin/dirname):

echo ${target_PWD%/*}

This will e.g. transform foo/bar/baz -> foo/bar

1 Comment

Unfortunately, readlink -f is a GNU extension, and thus not available on the BSDs (including OS X).
8
echo "$PWD" | sed 's!.*/!!'

If you are using Bourne shell or ${PWD##*/} is not available.

2 Comments

FYI, ${PWD##*/} is POSIX sh -- every modern /bin/sh (including dash, ash, etc) supports it; to hit actual Bourne on a recent box, you'd need to be on a mildly oldish Solaris system. Beyond that -- echo "$PWD"; leaving out the quotes leads to bugs (if the directory name has spaces, wildcard characters, etc).
Yes, I was using an oldish Solaris system. I have updated the command to use quotes.
7

Surprisingly, no one mentioned this alternative that uses only built-in bash commands:

i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"

As an added bonus you can easily obtain the name of the parent directory with:

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"

These will work on Bash 4.3-alpha or newer.

2 Comments

surprisingly ? just joking, but others are much shorter and easier to remember
This is pretty funny =P Had not heard of $IFS (internal field separator) before this. my bash was too old, but can test it here: jdoodle.com/test-bash-shell-script-online
6

There are a lots way of doing that I particularly liked Charles way because it avoid a new process, but before know this I solved it with awk

pwd | awk -F/ '{print $NF}'

Comments

5

i usually use this in sh scripts

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder

you can use this to automate things ...

1 Comment

readlink: illegal option -- f usage: readlink [-n] [file ...]
5

For the find jockeys out there like me:

find $PWD -maxdepth 0 -printf "%f\n"

1 Comment

Change the $PWD to "$PWD" to correctly handle unusual directory names.
4

Just use:

pwd | xargs basename

or

basename "`pwd`"

3 Comments

This is in all respects a worse version of the answer previously given by Arkady (stackoverflow.com/a/1371267/14122): The xargs formultion is inefficient and buggy when directory names contain literal newlines or quote characters, and the second formulation calls the pwd command in a subshell, rather than retrieving the same result via a built-in variable expansion.
@CharlesDuffy Nevertheless is it a valid and practical answer to the question.
@bachph, I disagree: A buggy answer (f/e, an answer that doesn't work when directory names contain spaces) should not be considered an answer at all.
3

Below grep with regex is also working,

>pwd | grep -o "\w*-*$"

1 Comment

It doesn't work if directory name contains "-" characters.
3

If you want to see only the current directory in the bash prompt region, you can edit .bashrc file in ~. Change \w to \W in the line:

PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

Run source ~/.bashrc and it will only display the directory name in the prompt region.

Ref: https://superuser.com/questions/60555/show-only-current-directory-name-not-full-path-on-bash-prompt

Comments

2

I strongly prefer using gbasename, which is part of GNU coreutils.

2 Comments

FYI, it doesn't come with my Ubuntu distribution.
@KatasticVoyage, it's there on Ubuntu, it's just called basename there. It's only typically called gbasename on MacOS and other platforms that otherwise ship with a non-GNU basename.
2

Here's a simple alias for it:

alias name='basename $( pwd )'

After putting that in your ~/.zshrc or ~/.bashrc file and sourcing it (ex: source ~/.zshrc), then you can simply run name to print out the current directories name.

Comments

1

An alternative to basname examples

pwd | grep -o "[^/]*$"

OR

pwd | ack -o "[^/]+$"

My shell did not come with the basename package and I tend to avoid downloading packages if there are ways around it.

Comments

0

You can use the basename utility which deletes any prefix ending in / and the suffix (if present in string) from string, and prints the result on the standard output.

$basename <path-of-directory>

Comments

0

Just remove any character until a / (or \, if you're on Windows). As the match is gonna be made greedy it will remove everything until the last /:

pwd | sed 's/.*\///g'

In your case the result is as expected:

λ a='/opt/local/bin'

λ echo $a | sed 's/.*\///g'
bin

Comments

-1

The following commands will result in printing your current working directory in a bash script.

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

1 Comment

(1) I'm not sure where we're ensuring that the argument list is such that $1 will contain the current working directory. (2) The pushd and popd serve no purpose here because anything inside backticks is done in a subshell -- so it can't affect the parent shell's directory to start with. (3) Using "$(cd "$1"; pwd)" would be both more readable and resilient against directory names with whitespace.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.