1117

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.

codeforester
  • 39,467
  • 16
  • 112
  • 140
Derek Dahmer
  • 14,865
  • 6
  • 36
  • 33
  • 2
    With full path, see: [Getting the source directory of a Bash script from within](https://stackoverflow.com/q/59895/55075). – kenorb Jul 19 '17 at 18:38

24 Answers24

1468

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=/

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
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 47
    What is the difference between ${PWD##*/} and $PWD? – Mr_Chimp Nov 22 '11 at 12:34
  • 36
    @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. – Charles Duffy Nov 25 '11 at 14:07
  • 3
    @Mr_Chimp - I was trying to set variable (and failing) with the initial `${pwd}`, and all full path variants. Friend no rush but if you can *maybe* add or comment back, clue me in on why `##*/` is needed for this to work? And the pwd in capitals, that really surprised me. Why does `result=${PWD#*/}` evaluate to /full/path/to/script and `result=${PWD##*/}` evaluates to script? Thanks again for the great answer. – Stephane Gosselin Mar 20 '13 at 06:11
  • 37
    @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. – Charles Duffy Mar 20 '13 at 12:20
  • 9
    Note that the output from `pwd` is not always the same as the value of `$PWD`, due to complications arising from `cd`ing 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. – Alex North-Keys May 17 '13 at 09:53
  • 8
    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!" – Red Cricket Oct 03 '13 at 22:17
  • How to print last but one directory name? eg: `/etc/usr/abc/xyz.txt printf '%s\n' "${PWD##*/}" #prints abc` How to print usr ? – Aparichith Dec 17 '14 at 05:03
  • 2
    @h.APP.y: `IFS=/ read -r -a dirs <<<"$PWD"; printf '%s\n' "${dirs[${#dirs[@]} - 2]}"` is the first approach that comes to mind. If you want better than that, I'd suggest asking as its own question. – Charles Duffy Dec 17 '14 at 05:32
  • -1 The negligible performance saved is a poor excuse for using string manipulation rather than re-using a standard function `basename`. It's less clean, and if you're that concerned about performance you shouldn't be using bash in the first place. – DBedrenko Mar 09 '17 at 11:06
  • 4
    @DBedrenko, if `basename` were a **function**, as opposed to an **external application** (and had a calling convention that didn't require a `fork()` and `mkfifo()` to invoke with output retrieved into a variable), I'd have no problem with it. It is, however, the latter. Moreover, unlike `dirname`, there are no corner cases that `basename` implements handling of: You're literally starting an external program to do some simple string manipulation, instead of doing it in-process, for no benefit whatsoever. – Charles Duffy Mar 09 '17 at 15:49
  • 3
    ...as for "negligible", it depends. If you're calling this in a loop going over 10,000 filenames, it's not negligible at all. – Charles Duffy Mar 09 '17 at 15:51
  • 2
    @DBedrenko ...well, I do need to walk back claims of identical results a *little*: `basename foo/bar/baz/` (with a trailing slash) is `baz`, whereas the parameter expansion result is an empty string. However, I'm inclined to argue that in the common case of working with a filename, the PE empty-string result is actually *more correct*: In the `basename` case you're getting a directory component, whereas in the string-manipulation case you're getting a result informing you (correctly!) that there **is** no filename in that path. – Charles Duffy Mar 09 '17 at 16:24
  • 2
    (...and `PWD` is guaranteed never to have a trailing slash, making that distinction meaningless in present circumstances). – Charles Duffy Mar 09 '17 at 16:32
  • 2
    I used this answer for tagging (adding a line to the top) of Claws Mail files, using the parent dir basename as described in this thread (mine): http://lists.claws-mail.org/pipermail/users/2017-March/thread.html#18865 . The timings are interesting, as shown in my gist, https://gist.github.com/victoriastuart/7457b2727d21a37b7a79eb9a957b988d . TLDR: [basename "$PWD"] is ~11-31-fold slower than [printf "${PWD##*/}\n"] ... – Victoria Stuart Mar 25 '17 at 19:04
  • You can also use any variable with this, doesn't have to be `$PWD`: `${SOME_VARIABLE##*/}` – luckydonald Apr 05 '17 at 09:10
  • @NickBull, `PWD` will **never** end in a slash. This question is specifically about "the current directory name", as opposed to being intentionally general. – Charles Duffy Oct 24 '18 at 13:20
  • No worries, this question came back while searching for getting the name of any directory in Google - I added that for this reason. You are correct that the question specifies only about the current directory however – Nick Bull Oct 24 '18 at 13:22
  • Makes sense that folks would want to apply it more broadly -- I've rewritten the addendum to be a bit more bash-centric (thus, appropriate to the shell we're tagged for), and explicit that it isn't needed in the `PWD` case. – Charles Duffy Oct 24 '18 at 13:28
  • Nice! I don't think you need `extglob` necessarily though :) – Nick Bull Oct 24 '18 at 13:34
  • Check my edit using negations! I think it's POSIX-compliant too, but I'm not 100% on that – Nick Bull Oct 24 '18 at 13:38
  • Neither works in https://ideone.com/GMMyLN. Frankly, these edits are a bit heavy-handed -- maybe you might add your own answer? – Charles Duffy Oct 24 '18 at 13:38
  • @CharlesDuffy Are you sure? It works for me on ideone.com - sorry if they are! I'm not sure it warrants a full answer – Nick Bull Oct 24 '18 at 13:40
  • 2
    See the link I provided -- both variants emit `/path/to/somewhere`, not `somewhere`. Or... *oh*, you were just demonstrating trimming, without the basename functionality? I misread the addendum; it could probably afford to be clarified. – Charles Duffy Oct 24 '18 at 13:42
  • Missed that! Yes :) I know it's a bit uglier than your initial elegant solution but it works with both trailing and non-trailing slashes in paths too which is pretty cool – Nick Bull Oct 24 '18 at 13:45
  • I find this answer excruciating. The following answers are SIMPLE: Arkady's (using basename), and DigitalRoss (using pure bash, just giving a one-line answer without blah). – juanmirocks Dec 01 '18 at 08:53
  • 4
    Bash is the wrong language if you don't want to learn details -- it's full of pitfalls and caveats, and if you refuse to learn them, you'll eventually end up with a script that *looked* like it worked perfectly fine deleting your backups when it encounters an unusual filename (or just a script that could run in seconds taking minutes, as you'll get if you use `basename` inside a tight inner loop). It's worth being willing to take some time to learn the details -- or picking a different, less finicky language. – Charles Duffy Dec 01 '18 at 16:39
  • 2
    (Yes, even something as simple as `echo "$foo"` itself can behave unpredictably, particularly when run on non-bash POSIX shells -- see APPLICATION USAGE and RATIONALE sections of http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html; note that that same behavior can happen in bash as well when the `xpg_echo` and `posix` flags are both set, and that both those flags can be set at compile-time as well as runtime... see again what I meant by "finicky"). – Charles Duffy Dec 01 '18 at 16:42
  • 2
    @Jas, see the parameter expansion link included in the answer. – Charles Duffy Apr 07 '20 at 12:34
  • I use this to kickstart docker commands in Makefile: `docker exec -it $${PWD##*/} /bin/bash` – Daniel W. Jul 02 '20 at 01:25
  • 1
    `result=${PWD##*/}` sets the variable incorrectly (or, at least, differently from `result=$(basename "$PWD")`) if the current working directory is the root directory, `/`. – pjh Jun 06 '22 at 14:35
  • 2
    @pjh, good point; adjusted accordingly. – Charles Duffy Jun 06 '22 at 14:53
  • I've found that you can handle the root directory `/` edge case in one parameter expansion with `${PWD##/*/}`. – Circumstantial Chord Aug 21 '23 at 17:35
542

Use the basename program. For your case:

% basename "$PWD"
bin
Arkady
  • 14,305
  • 8
  • 42
  • 46
  • 28
    Isn't this the purpose of the `basename` program? What is wrong with this answer besides missing the quotes? – Nacht Apr 30 '13 at 07:18
  • 20
    @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. – Charles Duffy Jun 29 '13 at 18:30
  • 6
    ...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. – Charles Duffy Oct 20 '14 at 12:29
  • This wins for simplicity, IMO. But this looks like current working directory has been saved to a variable (shouldn't that be $CWD). ` basename 'pwd' `, it literally can't get any simpler. (also, how do inline code with backticks in it?) – stands2reason Oct 14 '15 at 00:35
  • Makes for a nice function for working with compiled languages, "execute the file named after the working directory" - `function wdexec { ./$(basename "$PWD"); }` – Louis Maddox Nov 25 '15 at 17:10
  • 5
    @LouisMaddox, a bit of kibitzing: The `function` keyword is POSIX-incompatible without adding any value over the standardized syntax, and the lack of quotes would create bugs in directory names with spaces. `wdexec() { "./$(basename "$PWD")"; }` might be a preferable alternative. – Charles Duffy Jan 08 '16 at 03:36
  • Can anyone explains why PWD needs to be in capital letters, since low caps doesn't work – Paul Verschoor Oct 28 '16 at 10:30
  • @Rempargo That's just the environment variable that holds the current working directory: variable names are case-sensitive in bash. – DBedrenko Mar 09 '17 at 17:09
  • ...and all-caps names are reserved for variables with meaning to the OS and system (as `PWD` is), whereas applications are guaranteed to be able to use lowercase names for their own purposes without interference; see fourth paragraph of [the relevant POSIX spec](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html). If the shell exported a variable named `pwd` it would be breaking that specification by changing how an application using `$pwd` for its own purposes behaved. – Charles Duffy Jul 15 '17 at 17:54
  • 77
    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? – P.P Dec 05 '17 at 14:48
  • 3
    This is exactly what I needed, +1 for elegance and simplicity. – Austin Dean Apr 04 '18 at 18:00
  • 8
    @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). – Charles Duffy Oct 24 '18 at 13:34
  • 5
    Face-palm to see developers are arguing on the nicest answer. I'm taking side, kindly mark this answer as accepted one. – nehem Feb 04 '19 at 22:57
171
$ echo "${PWD##*/}"

​​​​​

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • 4
    @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##*/}"`. – Charles Duffy Dec 11 '12 at 03:04
  • 14
    @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). – Charles Duffy Jan 21 '13 at 17:16
33

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

#!/bin/bash

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

echo "$BASENAME"

exit;
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
mbelos
  • 473
  • 4
  • 8
  • 21
    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]. – Charles Duffy Sep 03 '09 at 03:26
  • 12
    and as a second style quibble backtics should be replaced by $(). still forks but more readable and with less excaping needed. – Jeremy Wall Sep 03 '09 at 21:17
  • 2
    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. – Rany Albeg Wein Jan 21 '16 at 04:09
  • 4
    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. :) – dannysauer Jan 27 '17 at 21:00
33

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.

Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
abc
  • 3,223
  • 1
  • 15
  • 15
20

How about grep:

pwd | grep -o '[^/]*$'
Orange
  • 225
  • 2
  • 2
  • 2
    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 – davidhq Sep 13 '18 at 22:56
13

This thread is great! Here is one more flavor:

pwd | awk -F / '{print $NF}'
rodvlopes
  • 895
  • 1
  • 8
  • 18
12
basename $(pwd)

or

echo "$(basename $(pwd))"
user3278975
  • 139
  • 1
  • 2
  • 3
    Needs more quotes to be correct -- as it is, the output of `pwd` is string-split and glob-expanded before being passed to `basename`. – Charles Duffy Jul 06 '14 at 16:49
  • 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. – Charles Duffy May 13 '20 at 22:18
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

FDS
  • 4,999
  • 2
  • 22
  • 13
8
echo "$PWD" | sed 's!.*/!!'

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

anomal
  • 2,249
  • 2
  • 19
  • 17
  • 5
    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). – Charles Duffy Feb 03 '14 at 14:06
  • 3
    Yes, I was using an oldish Solaris system. I have updated the command to use quotes. – anomal Feb 05 '14 at 01:11
6

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.

Arton Dorneles
  • 1,629
  • 14
  • 18
  • surprisingly ? just joking, but others are much shorter and easier to remember – codewandler Jul 08 '16 at 15:20
  • 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: https://www.jdoodle.com/test-bash-shell-script-online – Stan Kurdziel Feb 12 '17 at 05:50
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}'
geckos
  • 5,687
  • 1
  • 41
  • 53
5

For the find jockeys out there like me:

find $PWD -maxdepth 0 -printf "%f\n"
user2208522
  • 51
  • 1
  • 2
4

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

Dutch Glory
  • 23,373
  • 1
  • 17
  • 6
4

Just run the following command line:

basename $(pwd)

If you want to copy that name:

basename $(pwd) | xclip -selection clipboard

enter image description here

ahmnouira
  • 1,607
  • 13
  • 8
3

Just use:

pwd | xargs basename

or

basename "`pwd`"
fedorqui
  • 275,237
  • 103
  • 548
  • 598
marcos
  • 55
  • 1
  • 3
    This is in all respects a worse version of the answer previously given by Arkady (http://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. – Charles Duffy Jun 08 '15 at 00:55
  • @CharlesDuffy Nevertheless is it a valid and practical answer to the question. – bachph Mar 14 '20 at 09:53
  • @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_. – Charles Duffy Jun 06 '22 at 14:49
3

Below grep with regex is also working,

>pwd | grep -o "\w*-*$"
Abhishek Gurjar
  • 7,426
  • 10
  • 37
  • 45
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

Gautam Sreekumar
  • 536
  • 1
  • 9
  • 20
2

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

Steve Benner
  • 1,679
  • 22
  • 26
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.

Praxder
  • 2,315
  • 4
  • 32
  • 51
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.

D3M0N
  • 41
  • 4
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>
niks_4143
  • 87
  • 1
  • 7
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
Benjamin Zach
  • 1,452
  • 2
  • 18
  • 38
-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
octopusgrabbus
  • 10,555
  • 15
  • 68
  • 131
Paul Sabou
  • 3,097
  • 3
  • 16
  • 9
  • 3
    (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. – Charles Duffy Jun 13 '12 at 16:21