使用 grep 时跳过文件

使用 grep 时跳过文件

如何修改以下 bash 代码以使其按某个数值选择文件?例如,selcnt=3每三个文件搜索一次模式,selcnt=5每五个文件搜索一次模式,等等。

grep -r -l "${isufx[@]}" -e "$ptrn" -- "${fdir[@]}" |
  while read f; do
    echo -e $(tput setaf 46)"==> $f <==\n"$(tput sgr0)
    grep -ni "${ictx[@]}" -e "$ptrn" -- "$f"
    echo ""
  done

目的是加快搜索过程,但保持输出一次分成一个文件。加快进程的一种方法是针对不同的运行处理不同的文件,例如,通过跳过文件。

Run 1: Start from file 1 and skipping two files; 
Run 2: Start from file 2 and skipping two files;  
Run 3: Start from file 2 and skipping two files.   

作为第一次尝试,我使用了

ist=1; isk=2
grep --null -r -l "${isufx[@]}" -e "$ptrn" -- "${fdir[@]}"  |
  sed -z '${ist}~${isk}!d'  |
  while IFS= read -rd '' fl; do
    printf '%s\n\n' "${grn}==> $fl <==${sgr}"
    grep -ni "${ictx[@]}" -e "$ptrn" -- "$fl"
  done

但我收到错误

sed: -e expression #1, char 0: unmatched `{'

答案1

要从 的输出中选择每三个文件grep -l,首先请注意,您需要切换到 NUL 分隔列表以便能够处理任意文件路径(添加--null/-Z选项grep),然后您可以选择:

gawk -v RS='\0' -v ORS='\0' 'NR ~ 3 == 1'
sed -z '1~3!d' # assuming GNU sed
perl -0ne 'print if $. % 3 == 0'

然后,要循环该输出,它将是(使用 zsh 或 bash):

green=$(tput setaf 46) sgr0=$(tput sgr0)

while IFS= read -rd '' file; do
  printf '%s\n\n' "$green==> $file <==$sgr0"
  ...
done

您不想使用,echo -e因为这会破坏文件名中出现的反斜杠字符。

所以,把它们放在一起:

green=$(tput setaf 46) sgr0=$(tput sgr0)

grep --null -r -l "${isufx[@]}" -e "$ptrn" -- "${fdir[@]}" |
  sed -z '1~3!d' |
  while IFS= read -rd '' file; do
    printf '%s\n\n' "$green==> $file <==$sgr0"
    grep -ni "${ictx[@]}" -e "$ptrn" -- "$file"
  done

但如果重点是并行运行其中 3 个循环,每个循环处理 3 个批次中的 1 个,那么这就是 GNU 之类的东西的用途parallel

grep --null -r -l "${isufx[@]}" -e "$ptrn" -- "${fdir[@]}" |
  PARALLEL_SHELL=bash \
    GREEN=$(tput setaf 46) \
    SGR0=$(tput sgr0) \
    PTRN=$ptrn \
    parallel -m0kj3 '
    for file in {}; do
      printf "%s\n\n" "$GREEN==> $file <==$SGR0"
      grep -ni '"${ictx[@]@Q}"' -e "$PTRN" -- "$file"
    done'

在这里,通过环境变量传递标量变量,否则使用${param@Q}bash-4.4+ 将数组定义传递到内部 bash 实例(这里假设值不包含任何特殊的字符串 like parallel{}... {.}) 。

或者更好的是,避免上述限制:

grep --null -r -l "${isufx[@]}" -e "$ptrn" -- "${fdir[@]}" |
  PARALLEL_SHELL=bash TRANSFER_CODE=$(
    green=$(tput setaf 46) sgr0=$(tput sgr0)
    typeset -p green sgr0 ptrn ictx
    ) parallel -m0kj3 '
    eval "$TRANSFER_CODE"
    for file in {}; do
      printf "%s\n\n" "$green==> $file <==$sgr0"
      grep -ni "${ictx[@]" -e "$ptrn" -- "$file"
    done'

这次使用 的输出typeset -p将所有这些变量(数组或非数组)的定义传输到内部bash

parallel将并行启动 3 个bashshell,每个 shell 处理三分之一的文件,并在最后按顺序重新组合各自的输出。

无论如何,如果瓶颈是 I/O(从磁盘读取数据的速度),那么并行运行这些东西将无济于事。

相关内容