9

I just wanted to ask whether there is any command which would work on common shells (bash, dash, kornshell)? It is supposed to check if the line variable contains any part of the path.

if [[ $line =~ "$PWD"$ ]] ;then
applenic
  • 591
  • Did you try this? That command will work in modern bash shells just the way you typed it. – terdon Mar 27 '15 at 12:57
  • 1
    That won't work in dash. – Jonathan Leffler Mar 27 '15 at 14:06
  • In the description you simply say "the path" but in the code you use $PWD; do you mean "the canonical path to the current directory"?

    And what do you mean by "any part of"? Any sequence of directory components? Only the leftmost components? Only the rightmost components? Any substring without regard for directory components?

    – Martin Kealey Feb 02 '24 at 01:06
  • The use of regex matching seems unwarranted; [[ $line = *"$PWD" ]] would work just as well. – Martin Kealey Feb 02 '24 at 01:08

3 Answers3

15

In any POSIX-compatible shell you can do:

case $line in (*"$PWD"*)
#    whatever your then block had
;;esac

This works in bash, dash, and just about any other shell you can name.

It can also be used to handle multiple possibilities easily. For example:

case $line in 
(*"$PWD"*)
    echo \$PWD match\!
;;
(*"$OLDPWD"*)
    echo \$OLDPWD match\!
;;
(*)
    ! echo no match\!
;;esac

You can also use alternation:

case $line in (*"$PWD"*|*"$OLDPWD"*)
    echo '$OLDPWD|$PWD match!'
;;esac

Note the use of quoting above:

  1. case $line ...
    • The object of a case statement will not be split on either $IFS or be used as a pattern for filename gen. This is similar to the way the left argument in a [[ test is treated.
  2. (*"$PWD"*)
    • Here also a shell expansion is not subjected to either $IFS or filename generation - an unquoted expansion will neither split nor glob.
    • But an unquoted expansion here might be construed as a pattern rather than a literal string though, and so an expansion might mean more than one thing depending on whether or not it is quoted.
    • It is important to quote any variable used in a pattern that should be literally interpreted, in the same way you would quote pattern chars which you wanted interpreted literally.
    • For example, if $PWD contained a * and was not quoted it would be construed as a pattern object and not as a literal * to be searched for.
mikeserv
  • 58,310
  • @terdon - you were right - the : comment was confusing, but I used it because it didn't gen a syntax error or accidentally run some proggie named whatever in $PATH. This way will still gen a syntax error, but at least it's kinda clear that it should. – mikeserv Mar 29 '15 at 17:49
  • 2
    Note that if you need portability with the Bourne shell (for some ancient systems for instance), you need to drop the (: case $line in *"$PWD"*) – Stéphane Chazelas Mar 29 '15 at 21:35
  • @StéphaneChazelas - true, but if you do that, zsh chokes on it. – mikeserv Mar 30 '15 at 08:53
  • 1
    What do you mean? Are your referring to things like $(case a in a);; esac)? – Stéphane Chazelas Mar 30 '15 at 10:22
  • @StéphaneChazelas - I can't reproduce it, unless I do the command sub like in your example. It's weird too - zsh -cx '(case $1 in $2*) echo ok;;esac)' -- one o works, but prefixing a : $ breaks it. I guess that's the problem I encountered in the past - it was always a little unclear, and I never looked at it too hard because adding the leftmost ( always fixed it. Eventually I just wrote it off as some weird zsh thing and made it a rule to use both ends. – mikeserv Mar 30 '15 at 10:32
  • I prefer () as well as I like to have my braces matched, but it's still worth noting that Bourne compatibility implication. Note that $(case a in a);;esac issue used to be an issue in many shells (still is in pdksh (fixed in mksh) and ksh88 (fixed in ksh93)). I even believe it was the very reason to add support for the case string in (pattern) variant in the case syntax in ksh in the first place. – Stéphane Chazelas Mar 30 '15 at 10:50
  • @StéphaneChazelas - I don't doubt it. And it does make for easier reading - and I especially prefer it when generating case blocks for eval - in the latter case it is easy enough to get lost as is. And yes, the Bourne shell compatibility requirement is certainly noteworthy, but I think your comment sums it up well enough. Thanks again. I wonder if this particular issue w/ zsh actually is a consequence of its roots in pdksh - isn't that where it hails from originally anyway? It is based on some pre-93 ksh variant, I think. – mikeserv Mar 30 '15 at 10:55
  • No, zsh has nothing to do with pdksh or any ksh implementation (other than it has a ksh emulation mode). It was first released around the same time (89/90). It was once using readline for a wee while before it implemented its own line editor, but I don't think it's got any code in common with pdksh. – Stéphane Chazelas Mar 30 '15 at 11:21
  • @StéphaneChazelas - I know it's kinda weird, but will you look at this? I'm trying to bring this along to the point that I can do more w/ the information this provides than just print the bytes behind a keypress, but I'm confused about a couple of things. I think that, as is, it should handle multibyte sequences correctly - but I'm not sure my 7/8 byte buffer is enough. Also for whatever reason the only eight-bit meta (whatever that means) sequence that comes through is ALT+SPACE w/ octal 240. It's a little on topic... – mikeserv Mar 31 '15 at 12:12
4

Yes, recent versions of bash can do this:

$ pwd
/home/terdon
$ line="I'm in /home/terdon"
$ [[ "$line" =~ "$PWD"$ ]] && echo yes
yes

The same syntax works in zsh and ksh but not in dash. As far as I know, dash has no such capabilities.

Note that your regex is checking whether the variable $line ends with $PWD. To check if $PWD matches anywhere in $line, remove the $:

$ line="I'm in /home/terdon, are you?"
$ [[ "$line" =~ "$PWD" ]] && echo yes
yes
terdon
  • 242,166
  • 1
    No, those only work in bash-3.2 and above provided the compat31 option has not been enabled or ksh93. It won't work properly in other bash versions or zsh if $PWD contains regular expression operators. – Stéphane Chazelas Mar 29 '15 at 21:34
  • @StéphaneChazelas yes, that's why I specified that recent versions of bash can do this. As for the rest, I just tried it in /home/terdon/foo with zsh, ksh and bash (4.3.25) and it worked fine with all of them. It does indeed break when PWD contains regex operators. That had simply never occurred to me, thanks! – terdon Mar 29 '15 at 22:51
  • Why use =~ (regex) when = (aka ==, glob-match) will suffice? – Martin Kealey Feb 02 '24 at 00:56
  • @MartinKealey because the question asked for it. And = isn't equivalent to ==, the == is only supported in [[ ]] and not [ ] in bash. And if the pattern is a regex, you cannot always easily translate to globs. – terdon Feb 02 '24 at 11:16
  • @terdon I put = first precisely because == is less portable, yet if I don't mention it naïve users will offer the opposite quibble. The code in original question suggested =~ but the prose of the question did not mention regex, much less require it. I'm not assuming anything about translation from regex, but rather suggesting that simply writing a glob to begin with would be simpler for the objective of "match an embedded fixed string". https://xkcd.com/1171 – Martin Kealey Feb 04 '24 at 05:08
0

You can use a conditional line with shell substitution for this:

if [ "$line" != "${line#*$PWD}" ] ; then
    echo "Path found in line"
fi

This works by trying to remove the value of $PWD from $line, using shell parameter expansion. If the strings don't match, then the removal was successful, and the value of $PWD was in the line (ie the $line contained $PWD).

Note however, that this may not be semantically what you want, if you are doing path matching. For example if $PWD was '/bin', and $line was '/sbin:/usr/bin' you would get a false positive match on /usr/bin (because '/bin' matches that).

This is POSIX-compliant and should work in all shells.

Tim Bird
  • 1,121