使用shell读取ini文件方法步骤
作者:James Gosling
动机
我决定编写一个脚本来进行干净的 macOS (BSD) / Linux 的初始设置。 我认为有一个 tsukkomi 说“现在大多数发行版都预装了 perl / python”,但我决定使用 Shell 脚本(bash),因为它易于编写进程过程。 但是,用shell语法写各种配置文件是不可读的,所以我决定把配置文件写成.ini文件,用sed处理,然后加载到shell中。 关于使用 sed 解析 .ini 文件,如果你在 google 上搜索会出现各种示例,但我决定将其作为可以处理 .ini 文件(如)的规范,通过引用它们更具可读性。
.ini 类文件格式
- 在 [section] 中设置节。紧接在 [和紧接之前] 之后的空白字符序列(空格 / 制表符)不被视为部分名称的一部分。 (规定可以在节名及其前后的括号之间放置空格字符。)但是,节名不应包含换行符。
- 用 parameter = value 设置参数变量及其值。 = 前后可以有空格。假设参数名称不包括=。如果参数名称包含不能在 shell 变量中使用的字符,则在输出的 shell 变量名称中将这些字符替换为 _。
- 如果行尾有 \,则下一行被视为续行。如果一行的开头有 4 个或更多空格/制表符,则将其视为上一行的续行。
- 忽略 # ,; 中的行尾作为注释。
- 忽略不包含 [and’]’ 的行,或在行首以外的位置不包含 = 的行。
在 shell 脚本中处理 .ini 文件时要执行的操作
查看部分列表。
将参数作为 shell 变量读取。 然而,作为一种变体
1. 让特定部分的参数为 shell 变量。 在某些情况下,将其设置为 shell 函数中的局部变量或环境变量。
2. 让所有或部分部分的参数为 shell 变量。 shell变量名是’基于段名的前缀’+‘参数名’。 同样在这种情况下,将其设置为局部变量或环境变量。 基于节名的前缀是通过将节名字符串中的shell变量中不能使用的字符替换为_并在末尾添加_来生成的。
假定处理系统
- bash
- BSD sed(我不能使用方便的 GNU sed 扩展,但我想在干净的 macOS 上运行它。)但是,我使用带有 -E 选项的扩展正则表达式。 (因为如果不能使用一个或多个匹配元字符’+',sed 语句就会变长。)
处理连续行和注释似乎有更普遍的用途,所以我单独描述。 通过将代码添加到那里描述的 sample5.sed 来实现上述内容。
查看部分列表
这相对容易。 不要处理与节名格式不匹配的行。 您所要做的就是删除与节名格式和输出相对应的行中的’[‘,’]’ 和不必要的部分。 这时候需要注意的是,换行符不是随便删的,如单独描述的,匹配换行符以外的表达式([^ \ [:space:]]] | [[ : 空白: ]]) 被使用。
:begin $!N s/[#;]([^[:space:]]|[[:blank:]])*([^\\[:space:]]|[[:blank:]])(\n)/\3/ s/[#;]([^[:space:]]|[[:blank:]])*(\\)(\n)/\2\3/ $s/[#;]([^[:space:]]|[[:blank:]])*$// /(\\\n|\n[[:blank:]]{4})/ { s/[[:blank:]]*(\\\n|\n[[:blank:]]{4})[[:blank:]]*/ / t begin } /^[[:blank:]]*\n/ D /\n[[:blank:]]*$/ { s/\n[[:blank:]]*$// t begin } /^\[([^[:space:]]|[[:blank:]])*\]/! D s/\[[[:blank:]]*// s/[[:blank:]]*\]([^[:space:]]|[[:blank:]])*// P D
例如,如果您尝试将其作为示例 .ini 文件,
# -*- mode: conf-mode ; -*- \ #; #; sample .ini file #; #; [tako] param_a=(1 # 2 3 \ 4 5 ### 6 7 8 9 # 10 ) a=b # kani \ # kani \ [kani] param_a=1 param_b=2 [uni] param_a=3 param_b=4 [wani] param_a=5 param_b=6 [hebi] param_a=9 param_b=10 output example: % sed -nE -f list_section.sed sample.ini tako kani uni wani hebi
仅提取特定部分
下面的示例仅从上面的示例中提取特定部分 (wani) 的行,这些行遵循正确的参数定义格式。如果您找到以节名形式存在的一行,请将节名存储在保留空间中,否则如果保留空间的内容与所需的节名不匹配,则转到下一行。…如果匹配,则检查是否匹配参数格式的定义,去掉空格,本例中添加文本,使其成为shell函数的局部变量,可以总结shell变量定义。在行尾。因为已经变长了,所以加了注释行,但是作为控制结构,对每个处理都进行必要的处理,如果后面的处理没有必要,就直接回到开头. , 应该比较容易理解。
:begin $!N # Erase comment strings s/[#;]([^[:space:]]|[[:blank:]])*([^\\[:space:]]|[[:blank:]])(\n)/\3/ s/[#;]([^[:space:]]|[[:blank:]])*(\\)(\n)/\2\3/ $s/[#;]([^[:space:]]|[[:blank:]])*$// # Concatenate continuation lines /(\\\n|\n[[:blank:]]{4})/ { s/[[:blank:]]*(\\\n|\n[[:blank:]]{4})[[:blank:]]*/ / t begin } # Erase blank lines /^[[:blank:]]*\n/ D /\n[[:blank:]]*$/ { s/\n[[:blank:]]*$// t begin } # Check section headline and store section name to holdspace /^([^[:space:]]|[[:blank:]])*\[([^[:space:]]|[[:blank:]])*\]/ { h x s/^([^[:space:]]|[[:blank:]])*\[(([^[:space:]]|[[:blank:]])*)\].*$/\2/g s/^[[:blank:]]*//g s/[[:blank:]]$//g x D } # Skip line if current section is not interested one x /^wani$/! { x D } x # Print if it is proper parameter definition /^(([^[:space:]=]|[[:blank:]])*)=(([^[:space:]]|[[:blank:]])*)/ { s/^[[:blank:]]*/local / s/[[:blank:]]*=[[:blank:]]*/=/ s/(([^[:space:]]|[[:blank:]])*)[[:blank:]]*(\n)/\1;\3/ P } D
如何限定 shell 变量名
如关于您想要做什么的部分中所述,您想要添加从部分名称生成的前缀到 shell 变量,或者如果 .ini 文件中的参数名称包含不能在 shell 变量中使用的字符串, 适当转换。 作为一种简单的方法,有多次调用sed并处理的方法,但是如果可以的话,我觉得用一个进程号sed就可以处理更漂亮。 这里最大的限制是 sed 没有一个保存空间。 在高级脚本语言中,可以将文本段划分为多个变量,进行存储、处理和组合。 另一方面,在 sed 的情况下,标准方法似乎是将多个文本作为堆栈保存和使用,并将保留空间作为分隔符并带有换行符。
例如,在下面的示例中,保留空间从第一行开始。
- 部分名称
- 带格式化部分名称的前缀
- 模式空间第一行的备份
- 模式空间第二行的备份
- 根据.ini文件中的参数名格式化的Shell变量名
需要决定如何使用它,保持保持空间中的行数不变(下例中为 2 行),并在处理行转换时适当恢复模式空间。 由于交换保持空间和模式空间的命令只有gG和hH,类似的处理可能会重复出现,所以不可否认在1sed的过程中做起来真的很漂亮。
不管怎样,下面是从上面的 sample.ini 文件中提取 wani 和 uni 部分并输出添加了部分名称的 shell 变量的定义语句的示例。 整体控制结构保持简单,并且我添加了注释,所以我希望你能看到重写的地方以提取另一个部分,例如。
# Initialine the hold space: (Single empty line at the beginning) 1 { H ; x ; # Change the expression for the defalut section name and/or variable prefix, here. s/^([^[:space:]]|[[:blank:]])*(\n)([^[:space:]]|[[:blank:]])*$/global\2global_/g x } :begin $!N # Erase comment strings s/[#;]([^[:space:]]|[[:blank:]])*([^\\[:space:]]|[[:blank:]])(\n)/\3/ s/[#;]([^[:space:]]|[[:blank:]])*(\\)(\n)/\2\3/ $s/[#;]([^[:space:]]|[[:blank:]])*$// # Concatenate continuation lines /(\\\n|\n[[:blank:]]{4})/ { s/[[:blank:]]*(\\\n|\n[[:blank:]]{4})[[:blank:]]*/ / ; t begin } # Erase blank lines /^[[:blank:]]*\n/ D /\n[[:blank:]]*$/ { s/\n[[:blank:]]*$// ; t begin } # Check section headline and store section name to holdspace /^([^[:space:]]|[[:blank:]])*\[([^[:space:]]|[[:blank:]])*\]/ { # Remove blackets and extra spaces at first line s/^([^[:space:]]|[[:blank:]])*\[(([^[:space:]]|[[:blank:]])*)\](([^[:space:]]|[[:blank:]])*)(\n)/\2\6/g s/^[[:blank:]]*//g; s/[[:blank:]]*(\n)/\1/g # Store the section name to the hold space, and format stored one for shell variable for the hold space h x s/(\n)([^[:space:]]|[[:blank:]])*$// s/([^[:alnum:]_]|$)/_/g x # Append the section name to the hold space. H # Remove unused line of the hold space and rearrange the remaining lines. x s/(([^[:space:]]|[[:blank:]])*)(\n)(([^[:space:]]|[[:blank:]])*)(\n)(([^[:space:]]|[[:blank:]])*)$/\4\3\1/ x D } # Skip line if current section is not interested one x /^(wani|uni)(\n)/! { x ; D ; } x # Print if it is proper parameter definition /^(([^[:space:]=]|[[:blank:]])*)=(([^[:space:]]|[[:blank:]])*)/ { # Store current patern space text at the end of the hold space H # Build shell script variable name and store it at the end of the hold space s/(([^[:space:]=]|[[:blank:]])*)=.*$/\1/g s/^[[:blank:]]*// s/[[:blank:]]*$// s/[^[:alnum:]_]/_/g # If further rename of the variable name is necessary, put here. # Store variable name at the end of the hold space H # Build parameter variable value and keep it at pattern space # At first, Resore the current line text from hold space g # Remove unused lines. s/^(([^[:space:]]|[[:blank:]])*\n){2}// s/(\n([^[:space:]]|[[:blank:]])*){2}$// # Remove the text other than the parameter value s/^([^[:space:]=]|[[:blank:]])*=//g # If further formatting of the value is necessary, put here. # Append hold space stored date into pattern space G # Remove unused lines from the pattern space s/^(([^[:space:]]|[[:blank:]])*\n)(([^[:space:]]|[[:blank:]])*\n)(([^[:space:]]|[[:blank:]])*\n)(([^[:space:]]|[[:blank:]])*\n)(([^[:space:]]|[[:blank:]])*\n)/\1\5\9/ # Rearrance the order of line in the pattern space, it is nessacery because only \1 ...\9 is avaiable s/^(([^[:space:]]|[[:blank:]])*\n)(([^[:space:]]|[[:blank:]])*\n)(([^[:space:]]|[[:blank:]])*)(\n)(([^[:space:]]|[[:blank:]])*)$/\1\3\8\7\5/ # Format the output in the first line of the pattern space, and # Restore the next line at the second line of the pattern space s/^(([^[:space:]]|[[:blank:]])*)(\n)(([^[:space:]]|[[:blank:]])*)(\n)(([^[:space:]]|[[:blank:]])*)(\n([^[:space:]]|[[:blank:]])*)$/local \4\7=\1;\9/ # Clean up hold space x s/(\n([^[:space:]]|[[:blank:]])*){3}$// x P } D
output example:
% sed -n -E -f pickup_section2.sed sample.ini
local uni_param_a=3;
local uni_param_b=4;
local wani_param_a=5;
local wani_param_b=6;
Shell scripting
每次更改要提取的section或者切换输出格式(sh/csh、shell变量/局部变量/环境变量、变量名前缀的ON/OFF)都要重写sed文件很麻烦,所以命令行我准备了生成 sed 命令作为选项的 shell 脚本。 使用另一篇文章中的模板生成。 对于上面的seds,我还尝试了通过.ini文件的参数变量名(shell变量名)来选择输出。 (但是,如果指定了多个段名和变量名,组合不是唯一的,所以它可能不是一个很有用的功能。)
文件存储:
https://github.com/nanigashi-uji/parse_ini_sh
https://gitlab.com/nanigashi_uji/parse_ini_sh
如何使用
[Usage] % parse_ini.sh -list file [files ...] % parse_ini.sh [options] file [files ...] [Options] -l,--list : List sections -S,--sec-select name : Section name to select -T,--sec-select-regex expr : Section reg. expr. to select -V,--variable-select name : variable name to select -W,--variable-select-regex expr : variable reg. expr. to select -L,--local : Definition as local variables (B-sh) -e,--env : Definition as enviromnental variables -q,--quot : Definition by quoting with double/single-quotation. -c,--csh,--tcsh : Output for csh statement (default: B-sh) -b,--bsh,--bash : Output for csh statement (default) -s,--sec-prefix : add prefix: 'sectionname_' to variable names. -v,--verbose : Verbose messages -h,--help : Show Help (this message)
output example:
% parse_ini.sh --list sample.ini
tako
kani
uni
wani
hebi% parse_ini.sh -S kani -L sample.ini
local param_a=1;
local param_b=2;% parse_ini.sh -S kani -L -s sample.ini
local kani_param_a=1;
local kani_param_b=2;% parse_ini.sh -S kani -L -e -c sample.ini
setenv param_a 1;
setenv param_b 2;% parse_ini.sh -S kani -L -e -c -q sample.ini
setenv param_a "1";
setenv param_b "2";
到此这篇关于使用shell读取ini文件方法步骤的文章就介绍到这了,更多相关shell读取ini文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!