TAFFISH-USER-MANUAL-CN

“TAFFISH”的全称是“Tools And Flows Framework Intensify SHell”,其中的“FISH”也可以理解为“授人以鱼不如授人以渔(Teach a man to fish)”,这也是“TAFFISH”的设计哲学。

文档语言(Doc's Language):

1 介绍(Introduction)

1.1 什么是“TAFFISH”?

简单的从技术上来说“TAFFISH”是一个同时面向用户(仅使用)与开发者(开发自己的工具与流程)的可以轻松跨设备跨平台的“应用商店”。其主要由三部分组成:

那么如何使用“TAFFISH”呢?其实也很简单:

  1. 安装容器化管理软化(Apptainer/Podman/Docker);
    # 根据您的系统选择如何安装
    sudo apt update
    # 根据您的需求选择安装哪一个容器管理软件
    sudo apt install apptainer
    
  2. 安装“TAFFISH”;

    详见[2. 安装]

  3. 使用“taf”安装你想使用的“taf app”;
    taf install xxx
    
  4. 像使用普通命令行工具一样使用你安装的“taf app”吧!
    taf-xxx -h
    taf-xxx ...
    

    更详细的安装与使用教程会在[2. 安装][3. 快速开始(一个例子)]中展示

  5. (如果你是开发者)您还可以用任意编辑器(vim/emacs/...)编写您的“taf脚本”并运行/提交/...
    vim xxx.taf
    taffish xxx.taf ...
    taf ...
    

    更详细的内容可以参见《TAFFISH 开发手册》

也许您发现了,我们的 taffish 对于最简单的应用就是把他当作 apt/brew/conda 的“软件安装工具”的替代品,只不过 taffish 的安装是不破坏系统环境!无环境依赖!可移植!可复现的!

1.2 “TAFFISH”擅长什么?

目前“TAFFISH”主要面向的领域为生物信息学(Bioinformatics),因为该领域需要频繁地使用命令行工具并构建/使用命令行工作流,且经常会涉及跨设备,多设备的计算与合作等相关问题,而这正是“TAFFISH”所擅长的领域:

1.3 “TAFFISH”是否合适你?

“TAFFISH”目前适合的用户有如下:

1.4 “TAFFISH”的核心理念与逻辑

2. 安装

2.1 (可选但建议)安装容器管理软件

我们的“taf 软件仓库(taf-hub/taf-app-store)”中的大量软件都是基于容器管理软件所实现的,所以强烈建议先安装相关的容器管理软件!且建议安装 Apptainer(曾经叫做 Singularity,适合高性能计算需求的用户,但只支持 Linux),Podman(Docker 的无 root 版本,适合多用户/非root用户使用)以及 Docker(适合拥有 root 权限的个人 Windows/Mac 用户)。

我们建议去对应的官网来自行安装对应的容器管理软件:

同时,最好确保您的电脑中包含了“curl”,这样一会儿在下一步就可以使用在命令行通过“curl”实现自动安装了!("tafish" 也需要 curl 才能正常运行)

您可以根据您的系统自行选择使用 apt 或 brew 等包管理软件自行安装 curl

2.2 安装“TAFFISH”

目前我们的 taffish 只兼容如下操作系统(如果不包含您的设备,那么我们非常抱歉,您可以发送邮件提交您的设备情况(操作系统与硬件架构),我们会酌情考虑为您的设备增加适配的安装包!):

注:如果您需要为您计算机上的所有用户安装“TAFFISH”,那么请使用 root 账户或添加“sudo”命令来安装!

sudo sh -c "..." -n

注:您在安装过程中还可能会有一些需要更新/安装一些相关的库依赖的报错,您可以根据您的系统选择用 apt 或 brew 等包管理软件自行安装对应的依赖库。

您可以在最后添加参数来做一些不同的自动安装设定:

  • -n, --no-ask :: 默认安装,安装/更新软件但不覆盖配置文件等(所有问题自动跳过,使用默认选项)
  • -y, --yes :: (慎用)强制安装,安装/更新软件且强制覆盖配置文件等(所有问题选择“yes”选项)

2.3 测试是否安装成功

安装后您的系统内会拥有两个命令/可执行文件,也就是“taffish”(解释器)与“taf”(包管理系统),您可以输入以下代码分别检测是否安装成功:

taf -v; taffish -v

如果安装成功,那么应该会返回两个包含版本号的信息,类似如下信息(可能版本与更新日期会有差别):

taf      1.0.0-beta  KaiyuanHan(HermitHan)  2025-03-16
taffish  1.0.0-beta  KaiyuanHan(HermitHan)  2025-03-15

如果显示符合上述格式,那么恭喜你,安装成功啦!

你可以使用 “taf -h” 或 “taffish -h” 来查看更详细的关于 taf 与 taffish 的使用方法!
顺便说一句,您可以通过配置文件更改帮助语言(英文/中文)(root: /usr/local/etc/taffish/config.taffish.taf & local: ~/.config/taffish/config.taffish.taf)

2.4 (选读)taf 命令的用法

$ taf -h
taf      1.0.0-beta  KaiyuanHan(HermitHan)  2025-03-16
-----------------------------------------------
用法:
    taf [参数] [命令] ...
参数:
    -h, --help          帮助
    -v, --version       版本
命令:
    history                         <查看所有 taf 与 taffish 的命令使用历史>
    update-taf                      <更新 taf 和 taffish>    # 展示当前系统更新的命令
    search    [options] [app]       <从官网搜索应用>    # 支持正则表达式搜索
                  -a, --all          ... 额外展示每个应用包的详细说明
    install   [options] [app]       <安装应用>    # 非 root 用户仅安装在本地
                  -f, --file [file]  ... 从本地的 [app-file] 文件或应用压缩包安装模块
                  -y, --yes          ... 默认全部选项选择 "yes" 来安装(即存在时重新安装)
                  -n, --no           ... 默认全部选项选择 "no" 来安装(即存在时不安装)
    taf-xxx   [app-args]            <直接使用应用>    # 直接使用 taf-[app] 方式即可简单使用已安装应用
                  -h, --help         ... 展示该应用的帮助信息
    uninstall [options] [app]       <卸载应用>    # 非 root 用户仅卸载本地应用
                  -y, --yes          ... 默认全部选项选择 "yes" 来卸载
    upgrade   [options] [app]       <更新应用>
                  -y, --yes          ... 自动更新所有应用,不询问
    clean     [options] [应用]       <清理模块的本地环境(容器与镜像)>
                  [NULL]             ... 无参数时仅清理一些下载缓存
                  all                ... 删除所有已运行应用数据(删除内容仍然由 -a/--all 控制)
                  -a, --all          ... 无 -a/--all 时仅删除容器,有 -a/--all 时会将镜像一并删除
    apps      [options]             <展示当前已安装的所有应用>
                  -g, --global       ... 展示全局公共应用仓库
                  -l, --local        ... 展示个人本地应用仓库
    help/info [options] [app]       <查看某 app 的信息>    # 支持正则表达,有些应用可能没有信息
                  -g, --global       ... 从全局公共应用仓库中匹配模块
                  -l, --local        ... 从本地个人应用仓库中匹配模块
    pull      [options] [app]       <从已安装的模块仓库中获取应用>
                  -g, --global       ... 从全局公共应用仓库中获取
                  -l, --local        ... 从本地个人应用仓库中获取
* 更多信息请参考: https://taffish.com

更详细的内容可以参见《TAFFISH 开发手册》

2.5 (选读)taffish 命令的用法

$ taffish -h
taffish  1.0.0-beta  KaiyuanHan(HermitHan)  2025-03-15
-----------------------------------------------
用法:
    taffish [参数] [taf-file] [--args-name  args-value] ...
参数:
    -h, --help          帮助
    -v, --version       版本
    -t, --template      展示一个 taf 文件模板,帮助用户自己编写 taf 文件
    -n, --dry-run       干运行,仅展示转换后的 shell 命令
    -f, --force        [谨慎使用] 忽略错误,强制运行正确部分
    -s, --silent-run    静默运行,静默由 taffish 自动化运行的命令的运行输出
* 更多信息请参考: https://taffish.com

更详细的内容可以参见《TAFFISH 开发手册》

2.6 (选读)了解安装过程中“TAFFISH”干了些什么事儿

在我们的安装过程中,主要做了如下几件事:

  1. 创建 taffish 的主文件夹目录/结构
    1. 全局安装:/usr/local/share/taffish/
    2. 本地安装:~/.taffish/
  2. 安装可执行文件(taf & taffish)到特定的位置(环境路径)
    1. 全局安装:/usr/local/bin/
    2. 本地安装:~/.taffish/bin/

      本地安装可能需要您将对应的路径添加到您的 shrc 文件末尾即可(如“~/.bashrc”或“~/.zshrc”等),具体方法与流程在安装中会有对应的提示信息,按照操作执行即可,一般需要添加的代码为:

      export PATH=~/.taffish/bin:$PATH
      
  3. 为可执行文件适配自动补全脚本
    1. 全局安装:/etc/bash_completion.d/
    2. 本地安装:~/.taffish/completion/

      同上一步中的本地安装,本地安装中可能需要手动在 shrc 文件中添加 source 对应自动补全脚本文件的代码,具体操作会在安装过程中给出。且首次添加后,可能需要手动 source 或重启终端才能实现本地的自动补全。

  4. 添加 taffish 配置文件到特定的路径
    1. 全局安装:/usr/local/etc/taffish/
    2. 本地安装:~/.config/taffish/

    具体的配置文件有哪些,分别有什么作用,如何编写等详见《TAFFISH 开发手册》(#jump2config)

  5. (可选)添加 vim 高亮匹配(.taf 结尾文件)

    该步骤不一定可以成功,具体情况视操作系统适配性而定

3. 快速开始(一个例子)

现在,您已经成功安装了“TAFFISH”,接下来我们将通过一个简单的例子来帮助您更进一步理解和使用“TAFFISH”!

你好,我是小渔(fishka),我是一名生物信息学的研究人员,今天我的一位植物学家朋友给我拿来了一些数据:

  1. 一个植物样本(拟南芥AT)中检测到的一些蛋白质序列数据
  2. 与某种药用成分合成相关的蛋白家族(p450)的蛋白序列数据

这只是演示数据(阉割版),真实情况可能是某种未经过过多研究与测序的药用植物与其他蛋白家族或基因家族,或者患者体内的基因序列与某种疾病序列数据

他想要我从生信的角度去分析一下,拟南芥中哪些蛋白可能是p450家族的蛋白呢?并且最好可以直接给他对应的蛋白 ID,而不是复杂的文件信息!接下来我将向你展示我如何使用 taffish 来完成这样一个任务!

3.1 明晰解决思路与需要使用的工具

首先,我们应该先明晰如何解决这个问题,以及在这个问题中我们将使用到哪些生物信息学相关的工具:

其中第二步其实可以使用本地的 cut 与 uniq 等工具,但是这样可能会降低可移植性与可复现性,所以这里我们统一使用 taf-app 来完成全部步骤

那么接下来让我们开始动手解决问题。

3.1.1 先给出粗暴的解决代码

首先,在开始详细的流程前,我先粗暴的给出直接处理该问题需要的所有代码,后面再一步一步展开这个处理过程,好先给用户直观的一个概念:

taf update
taf install blast debian
taf-blast --cmd blastp --dbin ./p450.fasta --in ./at.fasta
taf-debian cut -f 1 ./blast-out/out.blastp_matches_my-blast-db.txt \| sort \| uniq

没错,从安装软件到使用工具到解决问题,一共只需要三行代码,且只要您正确安装了 taffish 与任何一个容器化软件(且设备“正常”联网),那么上述过程都可以轻松地在任何支持的设备上复现,且不会存在任何软件安装环境冲突等问题!

taffish 的软件安装并不同于其余软件安装,我们的软件安装只是从网上下载对应工具的 taf 脚本(纯文本文件),完全不涉及系统环境等,所以任意 taf-app 的安装过程基本都可以快速完成。

那么接下来让我们开始详细的展示上述过程背后的逻辑吧!

3.2 安装要用到的工具(taf install)

“TAFFISH”系统下的软件都是基于容器管理软件的,所以只要你至少正确安装了一个容器管理系统[2.1 (可选但建议)安装容器管理软件],那么我们就可以不用操心软件安装过程中的各种依赖或者环境等任何问题,只需在“Taf-Hub/App-Store”中找到对应的软件,然后用一行命令安装就可以啦!

taf update
taf search blast debian

其实 taf-blast 中就包含了 cut 与 uniq 等工具了,但并不是所有的这些 taf-app 环境都会包含那些通用工具

你可能会得到类似以下的输出结果:

[All apps searched]:
blast debian

可以看到,我们的“应用商店”中是拥有对应的工具的,那么让我们来快速安装这两个工具:

taf install blast debian

这一步如果已经安装过了,可能会询问你是否要覆盖并重新安装

如果顺利安装成功,可能会看了类似如下的信息:

[√] blast ..................................... [Installed]
[√] debian .................................... [Installed]

那么恭喜你,安装成功啦!那接下来我们可以开始使用这两个工具了:

3.3 使用 taf-blast 进行序列比对(taf-blast)

现在我们已经成功安装了 taf-blast 了,那么我们可以使用如下命令来看看该工具是如何使用的:

taf-blast -h

你可能会得到如下结果:

# <blast:latest | KaiyuanHan | 2025-01-06>
### Optional ##############################################################################
    <main>
        if ( echo '::dbin::' | grep "\.fasta$" > /dev/null 2>&1 ); then makeblastdb ::db-opts::; fi;
        ::cmd:: ::opts::
    <outdir>
        ::*WORKDIR*::blast-out
    <dbout>
        "::outdir::/my-blast-db/::dbtitle::"
    <db-opts>
        -in       ::dbin::       # (auto)                 for building blast-database
        -dbtype   ::dbtype::     # (default: auto)        database type ,prot=>protain,nucl=nucleic-acid
        -title    ::dbtitle::    # (default: my-blast-db) database title
        -out      ::dbout::      # (default: "./blast-out/my-blast-db/::dbtitle::") database output
        ::db-opts-add::
    <opts>
        -db               ::db::              # (need) database for blast
        -query            ::in::              # (need) blast seqs' file
        -out              ::out::             # (default: "./blast-out/out.cmd_matches_::dbtitle::.txt") output
        -evalue           ::evalue::          # (default: 1e-5) e-value
#       -num_aligntments  ::num-align::       # (default: 10)   seqs' number for blast
#       -max_target_seqs  ::blast-maxnum::    # (default: 4)    most target seqs' mapped number
#       -perc_identity    ::identity::        # (default: 90)   perc identity
        -num_threads      ::threads::         # (default: 4)    cpu threads
        -outfmt=::outfmt::                    # (default: 6)    output format: 0~18, usually 0,5,6,7
                                              # [0: same to online] [5: XML] [6: table] [7: table with anno]...
        ::opts-add::
    <out>
        "::outdir::/out.::cmd::_matches_::dbtitle::.txt"
### NEED ##############################################################################
    <cmd>
        # [blastn:DNA=>DNA-db] [blastp:Protain=>Protain-db] [blastx:DNA=>Protain-db] ...
    <dbin>
        # fasta file for building blast-database
    <in>
        # input fasta file
### RUN ##############################################################################
    <container:taf-blast:docker.io/ncbi/blast:latest>
        ::*MAIN*::

其实核心的内容就是 NEED 参数与 RUN 命令,我们只需要提供:

  1. cmd:我们是将使用什么对比工具(蛋白:blastp,核酸:blastn,……)
  2. dbin:用于对比的序列文件,即建库文件,也就是疾病序列信息文件
  3. in:要进行比对的序列文件,也就是患者的序列文件
  4. 其余的参数如果您有兴趣进行更详细的设置,也可以提供

所以我们就和使用任何其他命令行工具一样来使用 taf-blast:

taf-blast --cmd blastp --dbin ./p450.fasta --in ./at.fasta

从运行角度来说,taf 脚本会运行所有 RUN 中的代码,其中涉及的任何 ::xxx:: 参数,都可以使用 taf-cmd --xxx xxx-value 的方式从命令行给该参数赋值(无论是否在 ARGS 中给了默认值)(内置参数除外)

第一次运行可能会有一个从官方获取镜像的过程,之后再运行就不会又了。

本次运行我们展示了 taf-app 的“开发者建议用法”,也就是开发者进行过优化的内置用法,而不是该工具原本的用法,这种用法需要用户额外了解和学习工具用法,且支持对自己开发的工具进行了“优化”的 taf-app。在下个步骤中我们将展示另一种更普适,更常规的用法。

当我们运行完毕后我们使用 ls 应该会看到当前工作路径下多了一个文件夹 ./blast-out/,我们想要的关于两个数据的比对结果就在这个文件夹内:./blast-out/out.blastp_matches_my-blast-db.txt。我们可以使用 less -SN 命令或者您喜欢的命令查看我们的比对结果。

sp|Q9ASR3|C7091_ARATH	tr|Q0X087|Q0X087_SOLLC	38.178	516	307	5	6	517	8	515	7.07e-135	396
sp|Q9ASR3|C7091_ARATH	sp|O48786|C734A_ARATH	37.739	522	297	8	9	519	13	517	3.25e-133	392
sp|Q9ASR3|C7091_ARATH	sp|Q05047|SLS1_CATRO	38.760	516	296	8	15	518	16	523	1.95e-118	354
……

可见,blast 对每条 AT 序列都与 p450 库中的序列进行了比对,且可能一条 AT 序列与多条 p450 序列存在高度相似性。在上一步的 blast 中我们的默认参数已经筛选了相对高相似性的序列,所以我们现在只需要使用 cut 获取第一列的 AT 序列并使用 sort + uniq 去除重复即可以获得可能是 P450 的 AT 蛋白序列了!

3.4 使用 taf-debian 进行统计与数据展示(taf-debian)

如果在普通环境中,那么我们的代码应该是这样的:

cut -f 1 ./blast-out/out.blastp_matches_my-blast-db.txt | sort | uniq

但是为了尽可能保证可移植与客服现性,我们使用官方提供的 debian 镜像中的 cut sort 与 uniq 来实现,即:

taf-debian cut -f 1 ./blast-out/out.blastp_matches_my-blast-db.txt \| sort \| uniq

对比二者,我们一共做了两处变化:

  1. 在最前面增加了一个 taf-debian:这一步相当于将后面的代码放在该环境中运行,那么 docker 等容器化软件保证了该步骤等可移植与可复现性;
  2. 将管道符 | 变成了 \|,这样才可以将管道符也作为代码的一部分传递给 taf-debian 而不是直接被本地 shell 识别为管道符传递给本地的 sort 与 uniq

    所以如果你仍然使用 | 大概率也可以正常运行,只不过这样它使用的就是你本地的 sort 与 uniq 了

运行后我们可能会得到类似如下的结果:

sp|O65785|C71B3_ARATH
sp|P92994|TCMO_ARATH
sp|Q9ASR3|C7091_ARATH
tr|Q9LEX2|Q9LEX2_ARATH
tr|Q9STI1|Q9STI1_ARATH

至此,我们就完成了我们的植物学家朋友的任务啦!

3.5 (可选)使用 taffish 搭建流程(taffish)

但是如果他又来找我们呢?不如直接将上述流程打包,让他自己也可以实现上述步骤吧!平时我们很少将我们的工作直接分享给他们,让他们自己计算,因为大部分时候我们的电脑环境,软件安装都是大不相同的,如果想要让我们的脚本可以顺利在他们的设备上运行,往往还需要做很大的努力(环境监测,软件安装,吧啦吧啦……)。但是 TAFFISH 的出现将改变这一状况,我们只需要“容器管理软件”+“TAFFISH”,就可以将我们的工作在大部分设备上复现!

如果你没有给你的工具同时适配 x86 与 arm64 架构,那么这里可能会出现一点小小的兼容性问题

所以同样的,我们先上代码,再解释!

3.5.1 先给出 taf 脚本的代码

+FLOW:blast-get-IDs
ARGS
    <cmd>
        blastp
    <dbin>
        ./db.fasta
    <in>
        ./in.fasta
RUN
    <auto-flow>
        taf-blast --cmd ::cmd:: --dbin ::dbin:: --in ::in:: > /dev/null 2>&1
        taf-debian cut -f 1 ./blast-out/out.::cmd::_matches_my-blast-db.txt \| sort \| uniq

其实很容易发现,与本来的工作相比,我们只是做了如下改动:

  1. 给了一些默认值:甚至可以不给,当用户使用时再从命令行通过 --cmd ... 的方式传入也可以
  2. 设置了 RUN-<auto-flow> 这一标签:这样都不需要手动安装软件,会自动安装 taf-blasttaf-debian
  3. taf-blast 这一步我们在最后添加了代码:> /dev/null 2>&1,这清空了这一步在屏幕的输出,保证了屏幕上只输出我们想要的最终 ID 结果
  4. taf-debian 这一步修改了文件的路径,因为 taf-blast 的输出文件名会根据 cmd 而有一些更改(详见 taf-blast -h)

那么接下来我们只需要将这个脚本文件/代码复制粘贴发给我们的植物学朋友,然后让他下次直接运行就可以了!不需要额外安装任何软件,做任何环境配置,也不需要修改任何代码。我们就这样几乎直接把我们工作时简单的几行命令,变成了一个可移植,可复现,可轻松分享和安装的软件

不知道该案例是否让你更深入地理解了 TAFFISH 呢?如果你已经看到了这里,那么不妨去试试使用 TAFFISH 轻松使用,搭建,分享你的/别人的工具/流程和工作吧!

3.5.2 taffish 语法简介

现在我再简单给大家介绍一下 taffish 这种语言,如果在上述内容用有一些不理解的部分,那么我想在阅读了接下来的内容后,应该会更容易理解上述内容。

taffish 语言是一种类似标记语言,该语法很简单,就是通过换行分隔每一行的元素,然后以特定的标签(单行元素)来分隔功代码的功能结构,而其代码功能结构可以分为从上到下四级结构:

  1. 文件开头的 taf 声明:在文件开头,用 “+TOOL:tool-name” 或 “+FLOW:flow-name” 来声明当前是一个什么样的文件,这是必须的,且一般一个文件只会在文件开头出现一次;
  2. 一级标签:“ARGS”或“RUN”或“LOAD”等:该一级标签需要以完全大写的形式单独占用一行,其中我们本次只会用到“ARGS”与“RUN”,其实大部分 taf 脚本中也都只会涉及这两个一级标签;
    1. ARGS:这里是参数空间,存储了所有的参数及其默认值。但是需要注意,我们的 TAFFISH 内置了一些默认参数,他们可以直接通过 ::xxx:: 调用,而不需要在 ARGS 或从命令行传入,且无法通过任何方式修改和重新定义,他们有:
      1. *WROKDIR*:当前用户的工作路径
      2. *USER*:当前用户的用户名
      3. *CPUS*:当前设备的线程数量
      4. *CMD-ARGS*:本次运行的所有命令行参数(包括 taffish 配置参数,如 --force, --silent-run 等)
      5. *APP-ARGS*:本次运行中的 app 接受参数(不包含 taffish 配置参数,仅包含该 taf-app 接受的参数)
      6. *LOAD-DIR*:本次运行的 taffish 文件所在路径(如果是 taf-app,那就是该 app 的 taf 脚本所在路径,有时候一些运行相关代码会与该脚本成某种相对路径关系,通过该参数即可较好设置稳定的调用途径)
      7. *MAIN*:(1)如果用户有在调用 taf-app 后添加*APP-ARGS*,即后续参数,且该参数中的第一项是以 -- 开头的,那么该参数将被替换为 ::main::,此时需要用户自定义 ARGS-<main> 参数来自定义用法;(2)如果用户的后续参数的第一项并不是 -- 开头,那么将本参数替换为 ::*APP-ARGS*::,也就是直接将用户输入作为运行代码(3.4 中对 taf-debian 的使用就是依赖了这种方式);(3)如果用户没有给任何 app 参数,也就是以类似 taf-blast 这样的方式使用,那么会将本参数替换为 ::else::,用户可以通过 ARGS-<else> 设置无输入时的代码。

      以上就是主要的内置参数,建议在每一个 taf-app 中使用 *MAIN* 来编写代码!

    2. RUN:这里是运行/代码空间,这里储存了所有要运行的代码及其运行环境(tag),每一个 tag 都是一种对下方代码的处理方式,合理使用 tag 可以帮助我们简化我们的代码。
  3. 二级标签:由 <> 这样一对尖括号包括,包裹的内容具体的功能和含义在不同的一级标签下是不同的,现在简要阐述在“ARGS”与“RUN”这两个一级标签下的含义:
    1. ARGS:在“ARGS”下的二级标签内对应的内容是变量名,例如如果存在如下这样一段 taf 代码:
      +TOOL:test
      ARGS
          <xxx>
              -a     1
              --name 23
      ……
      
      那么你可以在该 taf 文件中任意其他地方通过 ::xxx:: 这样用一对 :: 双冒号的方式来包裹该 xxx 变量并将其替换到对应的位置,且需要注意的是,该二级标签下的内容,在进行变量替换时会自动将换行符替换为一个空格,也就是如果你有一段这样的代码:echo "::xxx::",那么在运行时他会被替换为:echo "-a 1 --name 23"。所以“ARGS”其实就是构成了一个参数替换系统;
    2. RUN:顾名思义,其实 RUN 就是我们将要运行的代码片段,所以 taf 语言其实有些类似一些静态语言,需要先声明变量(ARGS),再调用变量编写代码(RUN),只是有些不同的是,我们的变量是统一保存在“ARGS”标签下的,且我们运行时,可以使用“RUN”的二级标签来对代码做出不同的处理,最终所有的处理后的代码会保存到一个“shell脚本”中并运行(其实并不是真的有这个文件,而是由底层直接将 shell 代码传递给 bash 命令来运行):
      1. <local>/<sh>/<shell>:该标签下的代码会当作 shell 代码原封不动的复制到最终的 shell 脚本中;
      2. <container/apptainer/podman/docker($cmd):taf-container-name:docker-image>:该标签下的代码会根据您所提供的容器名称和docker镜像名称来添加自动化生成/运行容器的 shell 代码,并将该二级标签下的 shell 代码通过 heredoc 来传递给容器内的对应 cmd(默认为 bash);
      3. <flow>:该标签下的 shell 代码可以调用 taf-app,并将其转化为对应的 shell 代码嵌入到当前 shell 中,而不是简单的在当前 shell 中直接保留 taf-app 来交给 shell 运行;
      4. <auto-flow>:该标签相对于<flow>标签而言,会自动检测对应的 taf-app 是否以安装,如果未安装,将会自动安装对应的 taf-app;
  4. 三级内容:在二级标签下一般对应的就是直接的内容,该内容在不同的标签下也会有不同的含义和对应的不同的处理对待方式,在“ARGS”下是变量的值,在“RUN”下一般是代码。

    “RUN”下一般是代码,且一般是 shell 代码,但是对于<python>这样的标签下就是其他语言代码,甚至有一些自定义的二级“RUN”标签下可以是特殊的代码结构等。用户也可以通过编程定义自己的二级“RUN”标签,详见《TAFFISH 开发手册》

更多更详细的 taffish 语言语法等内容可以参见《TAFFISH 开发手册》