代码之家  ›  专栏  ›  技术社区  ›  user1625344

多个表达式是找到正确的工具吗?

  •  1
  • user1625344  · 技术社区  · 8 年前

    我有很多目录,其中包含一组程序的输出和数据文件。每个目录都有类似的内容,我的Go深度增加了4个级别。我的目标是识别所有在任何子目录中都有核心文件的目录,以及给定名称模式的大于1K的日志文件。日志文件不会与核心文件在同一个子目录中。

    我可以独立地为每个目标编写find命令,但是我所有组合表达式的尝试都无法产生任何结果。

    第一个命令:

    find \( \( -path "./SESS*" -name "log_snap_*" \) \( -size +1k\) \)
    

    第二个命令:

    find \( -path "./SESS" -regex "*core.[0-9]+\(.gz)*" \)
    

    如何编写一个测试来标识满足这两个条件的目录?

    1 回复  |  直到 8 年前
        1
  •  2
  •   Charles Duffy    8 年前

    对这个问题有不止一种合理的解释——下面是基于不同问题的答案。


    假设你的意思是“要么”标准…

    考虑以下设置:

    files_empty=(
      SESS/log_snap_1234  # ignored because not more than 1k in size
      SESS/ignoreme       # ignored because not matching either pattern 
      SESS/core.13.gz     # expected to be in results
      SESS/core.13        # expected to be in results
    )
    files_full=(
      SESS/log_snap_2345  # expected to be in results
    )
    
    { tempdir=$(mktemp -d /tmp/test.XXXXXX) && cd "$tempdir"; } || exit
    mkdir -p SESS bad
    touch "${files_empty[@]}"
    for f in "${files_full[@]}"; do
      dd if=/dev/zero of="$f" bs=1k count=2
    done
    

    如果在使用上述方法创建测试环境之后,我们在GNU中运行以下内容 find :

    find ./SESS \
       '(' '(' -name 'log_snap_*' -size +1k ')' \
        -o '(' -regextype posix-extended -regex ".*core[.][0-9]+([.]gz)?" ')' \
       ')' -print
    

    …我们得到了正确的结果:

    ./SESS/log_snap_2345
    ./SESS/core.13
    ./SESS/core.13.gz
    

    那么,发生了什么变化?

    • 不使用 -path 当您只需修改起始位置时,过滤正在搜索的位置。当你跑步时 find . -path './SESS/*' ,搜索 到处 在下面 . ,但如果结果不匹配,则丢弃结果 ./SESS/* ;与最初只搜索您关心的目录相比,这是非常低效的。
    • 使用 -o 指定或条件。
    • 分组运算符或显式 -a 当您想要的是一个和后续测试之间的,因为这是隐式行为。
    • 指定最终操作(例如 -print )显然是很好的形式。在你目前的情况下不是完全强制的,但是 是 在其他常见情况下(如使用 -prune )养成习惯从而减少出错的空间。
    • 不允许regex以 * ,自 * 表示“前一项的零个或多个”。在正则表达式的开头,有 是 没有优先项,因此此构造没有意义。
    • 在regex中,显式句点应写为 [.] 因为一个裸女 . 表示“任何字符之一”。

    如果你的意思是“两个”标准…

    我们实际上不需要进入 找到 这部分。一个警告:我故意避免正确处理文件名包含文字新行的情况。这可能发生。忽视它并不理想。

    无论如何,将两个shell函数作为两个不同的 找到 命令:

    find1_cmd() {
      printf '%s\n' \
        SESS/session_one/log_snap_1234 \
        SESS/session_one/log_snap_4567 \
        SESS/session_three/log_snap_8901
    }
    
    find2_cmd() {
      printf '%s\n' \
        SESS/session_one/core.1234.gz
        SESS/session_four/core.5678.gz
    }
    

    我们只能找到两个目录中的目录,如下所示:

    prep() {
      while IFS= read -r line; do
        printf '%s\n' "${line%/*}"  # remove the filename, leaving only the directory
      done | sort -u                # sort and uniq-ify the results
    }
    
    comm -12 <(find1_cmd | prep) <(find2_cmd | prep)
    

    你当然可以代替 while 循环使用任何其他方法来剥离文件名并只保留目录。重要的是,我们正在(1)生成每个命令找到的唯一目录的排序列表;(2)使用 comm 排除一个或另一个唯一的目录。

    见 BashFAQ #36 更多关于使用 康姆 .