开发者

How to test if a string starts with a prefix from a list of prefixes in a shell

I have a requiremen开发者_开发百科t in a shell script. I get this location information from a text file; it is always valid.

/opt/sasuapps/senny/publish/gbl/SANDHYA/drop1

I need to check if the directory is empty or not which I have done. If the directory is not empty, I need to delete the files and directory under that location.

As a part of security check, I would like to check if the drop location got from the file (/opt/sasuapps/senny/publish/gbl/SANDHYA/drop1) starts with any of the below.

/mnt/senny_publish/gbl
/mnt/senny/publish/gbl
/opt/sasuapps/senny/publish/gbl

If yes then only go ahead and delete; else don't do anything.

How can I compare the location given with those fixed strings?


This will work in bash and any other Posix-style shell, i.e., it's OK for systems where /bin/sh is not bash.

check () {
  [ "x$1" = x ] && return 1
  for pf in /mnt/senny_publish/gbl              \
            /mnt/senny/publish/gbl              \
            /opt/sasuapps/senny/publish/gbl; do
      suf="${1#$pf}"
      [ x"$pf$suf" = x"$1" ] && return 0
  done
  return 1
}

testcheck () {
  echo -n "$1" :
  if check "$1"; then
      echo OK
  else
      echo BAD
  fi
}

testcheck /how/now
testcheck /not/this
testcheck /mnt/senny_publish/gbl
testcheck /mnt/senny/publish/gbl
testcheck /opt/sasuapps/senny/publish/gbl
testcheck /mnt/senny_publish/gbl/a/b
testcheck /mnt/senny/publish/gbl/a/b
testcheck /opt/sasuapps/senny/publish/gbl/a/b

So...

/how/now :BAD
/not/this :BAD
/mnt/senny_publish/gbl :OK
/mnt/senny/publish/gbl :OK
/opt/sasuapps/senny/publish/gbl :OK
/mnt/senny_publish/gbl/a/b :OK
/mnt/senny/publish/gbl/a/b :OK
/opt/sasuapps/senny/publish/gbl/a/b :OK

By avoiding grep and other external programs, we keep execution entirely in the shell, avoiding fork's and exec's, and which may also give additional protection against XSS attacks. Still, it would be a good idea to filter metachars early.


Assuming you are using bash for your shell script:

if [ -n "$(echo $LOCATION|grep -lE '/mnt/senny_publish/gbl|/mnt/senny/publish/gbl|/opt/sasuapps/senny/publish/gbl')" ]
then
    # Contains one of these paths
else
    # Does not contain one of these paths
fi

If you have a longer list of paths to look through, you could dump them to a tempfile, one per line, and use grep -lEf tempFileWithPaths.txt


Using case is a lot cleaner:

case $d in
/mnt/foo/gbl/*|/mnt/bar/publish/gbl/*|/opt/sasu/bar/publish/gbl/*)
    rm -fr "$d"
esac

..for everyday use. This is POSIX-compliant and quick.

If you want to make it into a function, you could do:

# prefixed_by "$locn" "$safe_pfx" '/dir/list'
prefixed_by() {
    [ -n "$1" ] || return
    local i f=$1; shift
    for i; do
        i=${i%/}
        case $f in
        "$i/"*) return 0
        esac
    done
    return 1
}

So, if you know the list never has spaces or wildcard characters in it (ie: it's set directly in your script with a string literal) you could do:

readonly safe_list='/mnt/foo/gbl /mnt/bar/publish/gbl /opt/sasu/bar/publish/gbl'

..in initialisation, and then later:

prefixed_by "$d" $safe_list && rm -fr "$d"

I've used local in the function, which isn't specified in POSIX, though most modern shells have it: you can change that to fit your circumstance, as the rest is all POSIX-compliant.

In BASH, you don't have to use case in the for loop: you can just use [[ which pattern-matches, ie:

[[ $f = "$i"/* ]] && return

It also doesn't word-split nor perform any filename expansion. Quoting the "$i" is needed, however to ensure any wildcard characters like * or ? are not used as wildcards.


use sed command

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜