【问题标题】:find in directory that starts with dash在以破折号开头的目录中查找
【发布时间】:2011-05-16 05:23:00
【问题描述】:

find 将文件名开头的破折号解释为选项的开头。使用熟悉的-- 技巧不起作用,因为选项位于文件名之后,引用无效,并且用\- 替换第一个破折号也不起作用。通常鼓励用户在此类文件名前加上./,但如果我不知道给定路径是绝对路径还是相对路径,我该怎么办?

编辑:一种解决方案是find "$(readlink -f -- "$test_filename")",但它很难看。有更好的想法吗?

编辑 2:感谢您的建议。以下是这项工作产生的两个脚本:safe-find.shsafe-count-files.sh

【问题讨论】:

    标签: unix shell find escaping


    【解决方案1】:

    如果它在脚本中,您可以随时检查它。例如。对于 bash、ksh 或 zsh:

    if [[ "$DIR" = -* ]]; then
        find ./"$DIR"
    else
        find "$DIR"
    fi
    

    以更简洁的形式(对于 bash、ksh93 或 zsh):

    find "${DIR/#-/./-}"
    

    如果它们都应该是目录,您甚至可以使用脚本的参数来执行此操作:

    find "${@/#-/./-}"
    

    【讨论】:

    • 请注意,if [ "$DIR = -* ] 将匹配 DIR 到文字字符串 -*(如果当前目录中没有名称以 - 开头的文件)或可能导致语法错误(如果有的话)。
    • @Gilles: 是的...我不知道发生了什么——我从不使用 'test' 或 [ 在 bash 中。
    【解决方案2】:

    这可能看起来有点便宜,但我实际上推荐您已经找到的readlink 解决方法。根据Unix standard

    第一个以 a 开头的参数 '-' (...) 和所有后续参数 应被解释为 表达式

    所以-- 确实行不通。 thkala 的解决方案也可能有效,但我发现它的可读性较差。不过,如果您执行大量 find 调用,它可能会更快。

    【讨论】:

    • 我在 OP 编辑​​之前的回答确实涉及 readlink,因为它确实比我的第一个 oneliner 不那么深奥。它的一个缺点是它会启动另外两个进程(一个分叉的 shell 和 readlink 本身),并且如果您有多个目录作为要查找的参数,则将 readlink 应用于所有这些将至少需要一个循环和一些技巧。
    • @thkala:我自己才想出来的。 $(exec readlink -f -- "$file") 以牺牲可读性为代价阻止其中一个分叉。
    • @larsmans:thkala 的回答具有在没有readlink -f 的系统上工作的优势(只有 GNU、NetBSD 和 OpenBSD 有)。
    • @Gilles:MacOS X 10.6.4 有来自 GNU 的readlink。 thkala 的解决方案是否也适用于 NetBSD shell (ash)?我检查了它在 Korn Shell 中是否有效。
    • @larsmans:thkala 的第一个解决方案需要 [[ … ]] 构造(因此 bash/ksh/zsh),而第二个解决方案需要 ${…/…/…}(因此 bash/ksh93/zsh)。根据official documentation,OSX 10.6.4 没有readlink -f(它有FreeBSD readlink)。
    【解决方案3】:

    使用 glob 捕获破折号:

    find . -name '[-]*'
    

    编辑:更新以删除短语正则表达式,并引用 glob,以便它由 find 解释,而不是 bash(仅影响某些边缘情况)。

    【讨论】:

    • 这是通配符,不是正则表达式。
    • @DennisWilliamson 它可能是通配符,但它也绝对是一个正则表达式。有一个(微不足道的)字符类,有连接,还有一个 Kleene 星。如果角色类有一个更多的角色,就会有交替,它会拥有所有的东西。但是,我承认,从技术上讲,如果您将任何一系列字符用作一个正则表达式,那么它就是一个正则表达式。
    • @ParthianShot:shell 将所有 [-]* 处理为一个 glob,而不是正则表达式。这里的星号不是 Kleene 星,因为它并不意味着“零或更多”。在 glob 中,它的意思是“任何东西”作为通配符。这里的表达式表示一个连字符后跟任何序列,而不是零个或多个连字符。但是,应该引用它,以防止过早扩展。
    • @DennisWilliamson 哦,我知道这一点。我只是在迂腐;指出它 可以 被解释为有效的正则表达式,这与某些 glob 不同。例如,* 本身是正则表达式中的语法错误,而在 glob 中它是有效的。话虽如此,请支持您的评论,因为它是相关的,并且可能对偶然发现此答案的人有所帮助。
    【解决方案4】:

    这是一种适用于所有类 Unix 系统的方法,不需要特定的 shell 或非标准实用程序¹。

    case $DIR in
      -*) DIR=./$DIR;;
    esac
    find "$DIR" …
    

    如果您的位置参数中有一个目录列表并且想要处理它们,它会变得有点复杂。这是一个 POSIX sh 解决方案:

    i=1
    while [ $i -le $# ]; do
      case $1 in
        -*) x=./$1;;
        *) x=$1;;
      esac
      set -- "$@" "$x"
      shift
      i=$(($i + 1))
    done
    find "$@" …
    

    Bourne shell 和其他 pre-POSIX sh 实现缺乏算术和set --,所以有点丑。

    i=1
    while [ $i -le $# ]; do
      x=$1
      case $1 in
        -*) x=./$1;;
      esac
      set a "$@" "$x"
      shift
      shift
      i=`expr $i + 1`
    done
    find "$@" …
    

    ¹ readlink -f 在 GNU(Linux、Cygwin 等)、NetBSD ≥4.0、OpenBSD ≥2.2、BusyBox 上可用。它在 Mac OS X(截至 10.6.4)、HP-UX(截至 11.22)、Solaris (从 OpenSolaris 200906 开始)、AIX(从 7.1 开始)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-05
      • 1970-01-01
      • 1970-01-01
      • 2011-01-26
      • 2018-04-22
      相关资源
      最近更新 更多