快速从CLI中搜索Jupyter笔记本。
#python #datascience #jupyter #cli

我和我的同事写了大量的Jupyter notebooks。在其中有效搜索是一个普遍的问题。

jupyter笔记本是JSON文件,因此使用诸如grep之类的传统搜索方法很乏味。没有一点修补是ð

最初,我在Internet上找到了一个名为“ NBGREP”的脚本,但对我们来说并没有解决。所以,我写了自己的版本。

它需要jq进行JSON处理,而GNU则需要笔记本中的concurrent搜索。

无论如何,这些都是很棒的工具,对于数据科学家来说可能非常方便。 jq使得可以轻松地针对JSON文件编写查询,而parallel则可以用来以非常简单的方式同时执行任何类型的代码。

它们易于安装:

Debian和朋友:

sudo apt-get install jq parallel

macos

brew install jq parallel

您可以找到我的脚本作为要点here,或者如果由于某种原因无法平行安装,则是non-parallel version。我专门为这篇文章写了一个非并行版本,所以请通知我是否有问题。

因此,当您运行它时:

nbgrep 'read_[a-z]'

您会得到类似的东西:

./foo/bar.ipynb
        df_a = pd.read_csv(
        df_b = pd.read_csv(
./foobar/barfoo.ipynb
        G = obonet.read_obo(url)

(我必须重命名的大部分结果)

脚本:

#!/bin/bash
set -euo pipefail

catch() {
  echo "ERROR $1 occurred on $2"
}
trap 'catch $? $LINENO' ERR

pattern="${1? You must provide a search pattern}"

jupyter-search() {
  file="$1"
  pattern="$2"

  matches=$(< "$file" jq '.cells[].source[]' -r \
    | grep -P "$pattern" \
    | xargs -I '%' echo -e "\t%"
  )

  if [ ! -z "$matches" ]
  then
    echo "$file"
    echo "$matches"
  fi

}
export -f jupyter-search

find . \
  -type 'f' \
  -iname '*.ipynb' \
  -not -path '*/.ipynb_checkpoints/*'\
  | parallel jupyter-search {} "$pattern"

现在让我们看看脚本的工作原理。

#!/bin/bash
set -euo pipefail

#!/bin/bash只是告诉内核在哪里可以找到脚本的解释器。 set -euo pipefail是Bash的“严格模式”。没有它,bash将不会停止在错误-e上执行脚本执行或遇到未定义的变量-u-o pipefail将确保如果在管道中发生任何错误(非零退出代码),则整个管道将被视为错误。

catch() {
  echo "ERROR $1 occurred on $2"
}
trap 'catch $? $LINENO' ERR

通过以这种方式捕获错误,我们可以看到发生错误的行。

pattern="${1? You must provide a search pattern}"

这是您想在笔记本中找到的搜索模式。它可以是任何类似perl的正则表达方式。如果未提供搜索模式,该行还将提供有用的错误消息。

jupyter-search() {
  file="$1"
  pattern="$2"

  matches=$(< "$file" jq '.cells[].source[]' -r \
    | grep -P "$pattern" \
    | xargs -I '%' echo -e "\t%"
  )

  if [ ! -z "$matches" ]
  then
    echo "$file"
    echo "$matches"
  fi
}

这是一个bash函数定义。该函数有两个参数:$file,从第一个位置参数中读取,而搜索$pattern从第二个读取。让我们专注于搜索部分:

  matches=$(< "$file" jq '.cells[].source[]' -r \
    | grep -P "$pattern" \
    | xargs -I '%' echo -e "\t%"
  )

在此,管道的输出(由管道连接的命令|连接)将分配给matches变量。

管道的第一个命令将笔记本读取到jq JSON处理器中,该处理器提取所有代码单元。这些被管道输送到GREP命令中,该命令将给定的$pattern应用于类似Perl的Regexp -P。管道中的最后一个命令将列出grep的匹配项。

函数末尾的if语句将打印结果,鉴于$matches不是一个空字符串。

export -f jupyter-search

parallel将在子壳中执行给定代码,该代码不会从父壳(壳执行脚本本身)继承变量。因此,有必要导出先前定义的函数,以便在并行创建的子壳中访问。

find . \
  -type 'f' \
  -iname '*.ipynb' \
  -not -path '*/.ipynb_checkpoints/*'\
  | parallel jupyter-search {} "$pattern"

此代码将以.ipynb扩展名以不敏感的方式找到所有常规文件(不包括符号链接,目录和设备文件)。它将在您启动脚本的目录中递归搜索,仅省略.ipynb_checkpoints目录。找到的笔记本文件被流传输到parallel命令中,该命令将jupyter-search函数应用于它们,并在给定的搜索$pattern中以与您的CPU核心编号一样多。所有这些过程都会将其结果发送到您的标准输出中,但是并行处理它们不会混乱。

因此,总而言之,我们有一种非常快速的方法来从命令行中搜索jupyter笔记本电脑。我希望你们中的一些人会发现它有帮助。如果您对如何改进它有想法,我愿意接受建议。