在 crontab 上创建一个流编辑器命令,并每 15 分钟重写一个文件

Create a command on crontab that stream editor and rewrite a file every 15 minutes

假设我有一个文件,其模式与另一个文件相匹配:

file_names.txt

pfg022G
pfg022T
pfg068T
pfg130T
pfg181G
pfg181T
pfg424G
pfg424T

我想使用 file_names.txt 并使用 sed 命令进入 example.conf:

example.conf

{
  "ExomeGermlineSingleSample.sample_and_unmapped_bams": {
    "flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/pfg022G.unmapped.bam"],
    "unmapped_bam_suffix": ".unmapped.bam",
    "sample_name": "pfg022G",
    "base_file_name": "pfg022G.GRCh38DH.target",
    "final_gvcf_base_name": "pfg022G.GRCh38DH.target"
  },

sed 命令会将 example.conf 上的 pfg022G 替换为 pfg022T,这是 file_names.txt (sed s/pfg022G/pfg022T/) 中的下一项。此时的 example.conf 应该是这样的:

{
  "ExomeGermlineSingleSample.sample_and_unmapped_bams": {
    "flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/pfg022T.unmapped.bam"],
    "unmapped_bam_suffix": ".unmapped.bam",
    "sample_name": "pfg022T",
    "base_file_name": "pfg022T.GRCh38DH.target",
    "final_gvcf_base_name": "pfg022T.GRCh38DH.target"
  },

15 分钟后替换应该是 pfg022Tpfg068T 等等,直到 file_names.txt 中的所有项目都用完。

与周期性的 cron 作业相比,创建 daemon/background 进程可能更容易。

 while read str;
 do 
    sleep 900;
    sed -ri "s@(^\"flowcell_unmapped_bams.*gatk-workflows/src/ubam/)(.*)(\.unmapped\.bam\"\],.*$)@$str@;s/(^\"sample.name.*: \")(.*)(\",.*$)/$str/;s/(^\"base_file_name.*: \")(.*)(\.GRCh38DH.*$)/$str/" example.conf;
 done < file_names.txt &

通过while循环逐行读取file_names.txt的内容,将行读取为变量str。休眠 900 秒,然后在三个 sed 命令中使用这个 str 变量。在所有命令中,使用 -r 或 -E 启用正则表达式解释并将行分成三部分。替换第 1 部分的行,然后是变量 str 和第 3 部分。在末尾添加 & 到 运行 进程到后台。

对于我认为这将如何工作的逻辑,

  1. 创建一个 cronjob,或者如果您的服务器定期关闭,则创建一个 anacron 作业,每 15 分钟 运行 一个 bash 脚本。
  2. 在 bash 脚本中,您可以使用 if 语句,您可以使用 grep 测试 filenames.txt 中的每一行,哪一行存在于 example.conf 中, 如果该行存在则转到 filenames.txt 中的下一行。如果您在 file_names.txt 中的最后一个字符串处,那么 bash 脚本应该停止 运行 exit 命令
  3. 您将 运行 sed 命令替换您的字符串。我确实认为 replace 命令应该能够取代它。
  4. 如果您必须重新加载服务以加载修改后的配置,然后再添加此配置。

以下 crontab 会 运行 您的脚本每 15 分钟:

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
# |  |  |  |  |
# *  *  *  *  *   command to be executed
 15  *  *  *  *   /path/to/script

script阅读

#!/usr/bin/env sh
file1="file_names.txt"
file2="example.conf"
sed -i -e "$(awk '(NR>1){print "s/"p"/""/g"}{p=}' $file1 | tac)" example.conf

我们在这里使用的技巧是进行敬畏替换。文件 example.conf 始终只包含一个也在“file_names.txt”中的字符串。因此,如果您尝试从最后一个替换到前面,您只会进行一次替换。

我们在这里使用 awk 来构建一个 sed-脚本,并使用 tac 来反转它,这样我们只有一个匹配项:

$ awk '(NR>1){print "s/"p"/""/g"}{p=}' $file_names.txt
s/pfg022G/pfg022T/g
s/pfg022T/pfg068T/g
s/pfg068T/pfg130T/g
s/pfg130T/pfg181G/g
s/pfg181G/pfg181T/g
s/pfg181T/pfg424G/g
s/pfg424G/pfg424T/g

如果我们用上面的脚本做一个sed,我们总是会以pfg424T(最后一个条目)结束,因为它会找到一个匹配项(假设我们在第三个条目中pfg068T),因此 sed 将在此之后执行所有替换。但是,当我们颠倒顺序时(使用 tac),sed 只会找到一个匹配项。

我可能会提前在队列目录中生成所有文件,并让 cron 作业在每次调用时选择下一个文件。

awk 'NR==FNR { a[++n] = [=10=]; next }
    { file =  ".conf"
      for(i=1; i<=n; i++) {
        l = a[i]; sub("{{name}}", [=10=], l);
        print l >file }
      close file
    }' - file_names.txt <<\____
{
  "ExomeGermlineSingleSample.sample_and_unmapped_bams": {
    "flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/{{name}}.unmapped.bam"],
    "unmapped_bam_suffix": ".unmapped.bam",
    "sample_name": "{{name}}",
    "base_file_name": "{{name}}.GRCh38DH.target",
    "final_gvcf_base_name": "{{name}}.GRCh38DH.target"
  },
____

运行 在您的示例中 file_names.txt 创建以下文件:

pfg022G.conf    pfg068T.conf    pfg181G.conf    pfg424G.conf
pfg022T.conf    pfg130T.conf    pfg181T.conf    pfg424T.conf

内容如您所愿;这是 pfg0222G.conf:

{
  "ExomeGermlineSingleSample.sample_and_unmapped_bams": {
    "flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/pfg022G.unmapped.bam"],
    "unmapped_bam_suffix": ".unmapped.bam",
    "sample_name": "pfg022G",
    "base_file_name": "pfg022G.GRCh38DH.target",
    "final_gvcf_base_name": "pfg022G.GRCh38DH.target"
  },

现在,您的 cron 作业只需​​要将其中一个移动到 example.conf 并进行处理。当包含文件的目录为空时,您就完成了。

#!/bin/sh

for f in confdir/*.conf; do
  if [ -e "$f" ]; then
    # Safeguard against clobbering previous run
    if [ -e ./example.conf ]; then
      echo "[=13=]: example.conf is still there -- skipping this run" >&2
      exit 63
    fi
    mv "$f" ./example.conf
    exec your_main_script_or_whatever
    # Should never fall through to here, but whatever
    break
  else
    echo "[=13=]: directory empty -- aborting" >&2
  fi
done

为了避免竞争条件——如果之前的 cron 作业仍然是 运行,或者由于某种原因失败了,我们不想破坏它的输入文件。这需要 your_main_script_or_whatever 在完成时删除 example.conf。如果你不关心这个,也许你可以简单地从上面的脚本中删除保护条件。