我有一个包含 cidr 列表的文件。如何使用 bash 脚本创建以下格式的大 json 具有单个 CIDR
[{"source":"1.1.1.0/32","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":65535,"min":1},"sourcePortRange":{"min":521,"max":65535}}}]
具有多个 CIDR
[{"source":"1.1.1.0/24","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":55555,"min":10001},"sourcePortRange":{"min":521,"max":65535}}},{"source":"2.2.2.0/24","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":55555,"min":10001},"sourcePortRange":{"min":521,"max":65535}}},{"source":"3.3.3.0/24","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":55555,"min":10001},"sourcePortRange":{"min":521,"max":65535}}}]
我想用 200 个条目来完成。我对 json 如何工作一无所知。有人可以帮我吗我想在 bash 脚本中使用该 json 作为变量
输入 CIDR 文件类似于
1.1.1.0/22
2.2.2.0/24
5.5.5.0/21
6.6.0.0/16
我在 bash 脚本中尝试了给定的解决方案,但我接近\r
每个 CIDR
脚本 :
#!/bin/bash
lel=$(while read cidr ; do
jq -n --arg CIDR "$cidr" '[{"source":$CIDR,"protocol":"17","isStateless":true,"udpOptions": {"destinationPortRange":{"max": 65535,"min": 1},"sourcePortRange": {"min":521,"max": 65535} }}]'
done < lol)
echo $lel
哈哈文件:
1.22.0.0/15
1.38.0.0/15
1.186.0.0/15
14.96.0.0/14
输出
[ { "source": "1.22.0.0/15\r", "protocol": "17", "isStateless": true, "udpOptions": { "destinationPortRange": { "max": 65535, "min": 1 }, "sourcePortRange": { "min": 521, "max": 65535 } } } ] [ { "source": "1.38.0.0/15\r", "protocol": "17", "isStateless": true, "udpOptions": { "destinationPortRange": { "max": 65535, "min": 1 }, "sourcePortRange": { "min": 521, "max": 65535 } } } ] [ { "source": "1.186.0.0/15\r", "protocol": "17", "isStateless": true, "udpOptions": { "destinationPortRange": { "max": 65535, "min": 1 }, "sourcePortRange": { "min": 521, "max": 65535 } } } ] [ { "source": "14.96.0.0/14\r", "protocol": "17", "isStateless": true, "udpOptions": { "destinationPortRange": { "max": 65535, "min": 1 }, "sourcePortRange": { "min": 521, "max": 65535 } } } ]
答案1
您的数据和代码有两个主要问题:
- 您有一个 DOS 或 Windows 文本文件格式的输入文件。
- 您的代码创建多个单元素数组,而不是具有多个元素的单个数组。
您的输入文件lol
似乎是 DOS/Windows 格式的文本文件。这意味着当需要 Unix 文本文件作为输入的实用程序读取该文件时,每行\r
末尾都会有一个附加的回车符 ( )。
您应该将该文件转换为 Unix 文本文件格式。这可以通过例如来完成dos2unix
。
至于你的代码,你可以避免 shell 循环并jq
一次性读取整个文件。这允许您创建单个结果数组,而不是一组数组,每个数组都有一个对象,您的代码就是这样做的。
以下假设结果中顶级数组的元素之间唯一不同的是值source
(问题中没有任何内容解释应如何选择源端口和目标端口的最大值和最小值):
jq -n -R '
[inputs] |
map( {
source: .,
protocol: "17",
isStateless: true,
udpOptions: {
sourcePortRange: { min: 521, max: 65535 },
destinationPortRange: { min: 1, max: 65535 }
}
} )' cidr.txt
或者采用与您的问题相同的紧凑单行形式:
jq -n -R '[inputs]|map({source:.,protocol:"17",isStateless:true,udpOptions:{sourcePortRange:{min:521,max:65535},destinationPortRange:{min:1,max:65535}}})' cidr.txt
使用inputs
,jq
读取剩余的输入。与 一起-R
,它将把 的每一行读取cidr.txt
为单个字符串。将其放入数组中,[inputs]
我们创建一个字符串数组。该map()
调用从此数组中获取每个字符串,并将其转换为source
更大的静态对象的值。
添加-c
到 的调用中jq
以获得“紧凑”输出。
如果您不想或无法将输入数据从 DOS 转换为 Unix 文本形式,则可以从表达式中删除回车符jq
。
为此,请将.
after替换source:
为(.|rtrimstr("\r"))
,包括外括号。这将从文件中读取的每个字符串末尾删除回车符。
答案2
回答
这应该可以为您提供所需的确切语法:
在示例中,包含 CIDR 值的文件已命名cidr.txt
,并且似乎仅包含 IP 地址和子网,即其他参数保持不变。如果您确实需要更改这些附加参数(即您提供的端口范围实际上对于所有 cidr 来说并不相同,那么我将更新我的答案,并提供一个完整的模板)
此外,您还需要 'jq' ,它是通过 bash 处理 JSON 的普遍应用程序。这些天可能已经安装了,但如果没有安装,那就sudo apt install jq
照常安装。
while read cidr ; do
jq -n --arg CIDR "$cidr" '{"source":$CIDR,"protocol":"17","isStateless":true,"udpOptions": {"destinationPortRange":{"max": 65535,"min": 1},"sourcePortRange": {"min":521,"max": 65535} }}'
done < cidr.txt | jq --slurp
使用您提供的四行文件示例,上面的输出将在终端中提供以下内容:
[
{
"source": "1.1.1.0/22",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 65535,
"min": 1
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
},
{
"source": "2.2.2.0/24",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 65535,
"min": 1
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
},
{
"source": "5.5.5.0/21",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 65535,
"min": 1
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
},
{
"source": "6.6.0.0/16",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 65535,
"min": 1
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
}
]
更新
为了修复上述输出,您需要“修复”CIDR 文件的行终止。有两种方法可以做到这一点:
答案1:
您可以对脚本进行以下更改
#!/bin/bash
# There are four changes made to the script:
# 1. The addition of `tr` in order to eliminate '\r'.
# 2. Removal of '[' and ']' inside the `jq` command.
# 3. Addition of `jq --slurp` to enforce your specified JSON format.
# 4. Addition of double-quotes around `$lel` to prevent splitting.
lel=$(while read cidr ; do
cidr=$(echo "$cidr" | tr -d '\r' );
jq -n --arg CIDR "$cidr" '{"source":$CIDR,"protocol":"17","isStateless":true,"udpOptions": {"destinationPortRange":{"max": 65535,"min": 1},"sourcePortRange": {"min":521,
"max": 65535} }}'
done < lol | jq --slurp )
echo "$lel"
替代答案
您可以“修复”包含 CIDR 列表的文件:
cp lol lol_old
cat lol_old | tr -d '\r' > lol
然后,您可以使用脚本的早期版本,尽管上面包含的脚本的 #2-4 注释中解释了更正。
解释
\r
在输出中找到的原因实际上是在包含 CIDR 的特定文件的格式中找到的,该文件恰好遵循 Windows(而不是 Unix)行终止标准。
\r
您在输出中看到的符号实际上也存在于源文件中,它用于终止\n
每一行。和\r
都是\n
不可见字符。这种组合\r\n
被称为 CRLF(回车+换行),这是打字机时代的残余,但由于某种原因 Windows 系统仍在使用。另一方面,Unix 仅使用 LF 来终止行,它以\n
转义形式表示。
要确认这种特殊行为,您可以尝试执行以下命令:
head -n 1 lol | xxd -ps
312e312e312e302f32320d0a
在上面的输出中 - 文件的第一行转换为十六进制形式 - 以0d0a
.该十六进制组合代表 CR+LF。另一方面,如果您直接在 Bash 终端内执行以下命令:
echo "abcd" | xxd -ps
616263640a
你会发现输出遵循Unix标准,其中行终止符使用 simple 0a
,即 LF 的十六进制表示。
笔记:这种行终止问题非常常见、广泛存在,并且在 Unix 内部对 Windows 下生成的文件进行操作时始终需要警惕。
有关信息jq
上面的示例(循环while read
)将其输出发送到终端,但是如果您需要使用标准语法将其存储在文件中,您当然可以使用重定向:
while read cidr; do [...] ; done < cidr.txt > outcidr.json
该文件将包含打印精美的 JSON 输出,但如果您需要/希望输出限制为单行,您可以执行以下操作:
cat outcidr.json | tr -d '\n' | tr -s ' '
更重要的是,如果您将来最终得到一个看起来无法破译的单行、复杂的 JSON 输出,jq
可以用来重新格式化和漂亮地打印它:
echo '[{"source":"1.1.1.0/24","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":55555,"min":10001},"sourcePortRange":{"min":521,"max":65535}}},{"source":"2.2.2.0/24","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":55555,"min":10001},"sourcePortRange":{"min":521,"max":65535}}},{"source":"3.3.3.0/24","protocol":"17","isStateless":true,"udpOptions":{"destinationPortRange":{"max":55555,"min":10001},"sourcePortRange":{"min":521,"max":65535}}}]' > bad_output.json
cat bad_output.json | tr -d '\r' | jq ''
[
{
"source": "1.1.1.0/24",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 55555,
"min": 10001
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
},
{
"source": "2.2.2.0/24",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 55555,
"min": 10001
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
},
{
"source": "3.3.3.0/24",
"protocol": "17",
"isStateless": true,
"udpOptions": {
"destinationPortRange": {
"max": 55555,
"min": 10001
},
"sourcePortRange": {
"min": 521,
"max": 65535
}
}
}
]
# Getting first-order keys for each of the 3 objects
jq '.[] | keys' bad_output.json
[
"isStateless",
"protocol",
"source",
"udpOptions"
]
[
"isStateless",
"protocol",
"source",
"udpOptions"
]
[
"isStateless",
"protocol",
"source",
"udpOptions"
]
# Getting values corresponding to the selected key"
jq '.[] | .source ' outcidr.txt
"1.1.1.0/22"
"2.2.2.0/24"
"5.5.5.0/21"
"6.6.0.0/16"