如何测试字符串是否以shell中前缀列表中的前缀开头

时间:2009-11-10 04:13:32

标签: shell scripting substring

我在shell脚本中有一个要求。我从文本文件中获取此位置信息;它总是有效的。

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

我需要检查目录是否为空,或者我已经完成了。如果目录不为空,我需要删除该位置下的文件和目录。

作为安全检查的一部分,我想检查从文件(/ opt / sasuapps / senny / publish / gbl / SANDHYA / drop1)获取的放置位置是否以下面的任何一个开头。

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

如果是,则只进行删除;别的什么都不做。

如何比较给定的位置和那些固定的字符串?

4 个答案:

答案 0 :(得分:3)

这将适用于bash和任何其他Posix风格的shell,即,对于/ bin / sh不是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

因此...

/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

通过避免grep和其他外部程序,我们将执行完全保留在shell中,避免使用fork和exec,并且还可以提供针对XSS攻击的额外保护。尽管如此,早期过滤metachars是个好主意。

答案 1 :(得分:2)

假设您正在为shell脚本使用bash:

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

如果要查看更长的路径列表,可以将它们转储到临时文件中,每行一个,然后使用grep -lEf tempFileWithPaths.txt

答案 2 :(得分:2)

使用case更清洁:

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

..适合日常使用。这符合POSIX标准且速度快。

如果你想把它变成一个函数,你可以这样做:

# 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
}

所以,如果你知道,列表中永远不会有空格或通配符(即:它直接在你的脚本中用字符串文字设置),你可以这样做:

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

..在初始化时,然后是:

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

我在函数中使用了local,但在POSIX中没有指定,尽管大多数现代shell都有它:你可以根据自己的情况进行更改,因为其余的都符合POSIX标准。< / p>

在BASH中,您不必在case循环中使用for:您可以使用[[模式匹配,即:

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

它也不会分词,也不会执行任何文件名扩展。引用"$i"是必要的,但要确保*?之类的任何通配符都不会用作通配符。

答案 3 :(得分:0)

使用 sed 命令