25

E.g. check if $PWD is a subdirectory of /home. In other words I'm searching for a bash string operation to check if one string starts with another.

25

If you want to reliably test whether a directory is a subdirectory of another, you'll need more than just a string prefix check. Gilles' answer describes in detail how to do this test properly.

But if you do want a simple string prefix check (maybe you've already normalized your paths?), this is a good one:

test "${PWD##/home/}" != "${PWD}"

If $PWD starts with "/home/", it gets stripped off in the left side, which means it won't match the right side, so "!=" returns true.

  • 1
    +1 perfect, and for a more general case: [ "${PWD##$VAR}" != "$PWD" ] And if this were a [code-golf ](stackoverflow.com/questions/tagged/code-golf)question you'd have beaten me by two chars ;-) Also it looks more readable... – Tobias Kienzler Jan 26 '11 at 11:16
  • FWIW, this may be also useful: ${PWD/#$HOME/\~} – user1338062 Nov 1 '15 at 8:24
  • Alright, but how about PWD="/home/../etc/" – Alexis Peters Mar 22 '16 at 14:21
  • 1
    @AlexisPeters: You're right: I was focusing on the "string prefix" part of the question, and I have edited to make that more clear. Gilles has nicely covered the proper way to check for subdirectories. – Jander Mar 22 '16 at 20:34
  • 1
    Use "${PWD%%/subdir*}" to detect whether the user is currently in subdir or in a subdirectory of subdir, since %% captures from the end of the string instead of the beginning. This should be useful to anyone looking for more info on parameter substitution: tldp.org/LDP/abs/html/parameter-substitution.html – George Pantazes Jan 23 '18 at 19:21
33

To test if a string is a prefix of another, in any Bourne-style shell:

case $PWD/ in
  /home/*) echo "home sweet home";;
  *) echo "away from home";;
esac

The same principle works for a suffix or substring test. Note that in case constructs, unlike in file names, * matches any character, including a / or an initial ..

In shells that implement the [[ … ]] syntax (i.e. bash, ksh and zsh), it can be used to match a string against a pattern. (Note that the [ command can only test strings for equality.)

if [[ $PWD/ = /home/* ]]; then …

If you're specifically testing whether the current directory is underneath /home, a simple substring test is not enough, because of symbolic links.

If /home is a filesystem of its own, test whether the current directory (.) is on that filesystem.

if [ "$(df -P . | awk 'NR==2 {print $6}')" = "/home" ]; then
  echo 'The current directory is on the /home filesystem'
fi

If you have the NetBSD, OpenBSD or GNU (i.e. Linux) readlink, you can use readlink -f to strip symbolic links from a path.

case $(readlink -f .)/ in $(readlink -f /home)/*) …

Otherwise, you can use pwd to show the current directory. But you must take care not to use a shell built-in if your shell tracks cd commands and keeps the name you used to reach the directory rather than its “actual” location.

case $(pwd -P 2>/dev/null || env PWD= pwd)/ in
  "$(cd /home && { pwd -P 2>/dev/null || env PWD= pwd; })"/*) …
  • +1 Thanks for this exhaustive answer. I didn't consider links and filesystem peculiarities when asking this, but it's definitely good to know – Tobias Kienzler Jan 26 '11 at 11:08
7

Crude version:

[ ${PWD:0:6} = "/home/" ]

Has the disadvantage that one has to count characters first and one can't replace /home/ by something general like $1.

edit (thanks @Michael) for the generalization to compare with $VAR one can use

[ "${PWD:0:${#VAR}}" = $VAR ]
  • 4
    ${#var} is the length of $var, so you can generalize it as [ "${PWD:0:${#1}}" = "$1" ] – Michael Mrozek Jan 25 '11 at 15:58
  • @Michael Mrozek♦: Thanks, works perfect :) – Tobias Kienzler Jan 26 '11 at 11:03
2

I don't understand the question too well, but to find the parent of $PWD, do dirname $PWD. To find the parent of the parent, run dirname $(dirname $PWD), and so on...

  • That would only work if I knew the depth, otherwise I end up with /. Ok, I could check recursively, but isn't there something easier? – Tobias Kienzler Jan 25 '11 at 15:41
  • 1
    @tob If only I knew shell programming ;-) – Tshepang Jan 25 '11 at 16:06
1

Using awk:

echo $PWD | awk -v h="/home" '$0 ~ h {print "MATCH"}'
  • +1 for working, but a bit slower than a native bash test – Tobias Kienzler Jan 26 '11 at 11:13
0

Hm, it's pity that [ doesn't have an option of testing STRING1 starts with STRING2 condition.

You may try echo $PWD | grep '^$VAR', but it can fail in interesting ways when VAR contains special symbols.

awk's index function should be able to do the trick. But all this seems just too heavy for such an easy thing to test.

  • [[ does regexp so yo get startswith [[ $PWD =~ ^\/home\/ ]] – teknopaul Dec 2 '15 at 23:32
0

If the searched part of path is found I "empty" the variable :

[[ -z "${PWD//*\/home\/*/}" ]] && echo "toto"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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