问题描述
如何编写 bash for 循环来创建模板
源文件:
"INTEL SSDPEL1D380GA CCCCCCCCCCCCCCC 01","Samsung SSD 970 PRO 1TB XXXXXXXXXXXXXXX 01","Samsung SSD 970 PRO 1TB YYYYYYYYYYYYYYY 01","Samsung SSD 970 PRO 1TB ZZZZZZZZZZZZZZZ 01","Samsung SSD 970 PRO 1TB IIIIIIIIIIIIIII 01"
期望输出:
node_nvme_device{manufacturer="INTEL",partnumber="SSDPEL1D380GA",serialnumber="CCCCCCCCCCCCCCC"} 1
node_nvme_device{manufacturer="Samsung",partnumber="SSD 970 PRO 1TB",serialnumber="XXXXXXXXXXXXXXX"} 1
node_nvme_device{manufacturer="Samsung",serialnumber="YYYYYYYYYYYYYYY"} 1
node_nvme_device{manufacturer="Samsung",serialnumber="ZZZZZZZZZZZZZZZ"} 1
node_nvme_device{manufacturer="Samsung",serialnumber="IIIIIIIIIIIIIII"} 1
我尝试和失败的代码
awk 'NF>1 {
"node_nvme_device{manufacturer="$1",partnumber="$2",serialnumber="$3"} 1" }' source_file
解决方法
可以使用单线,例如Perl 但使用 shell 脚本,您将拥有更多/更好的灵活性、可扩展性、维护代码供以后使用用例。
正确的解决方案将分为两个主要步骤:
- 首先将文件读入数组
- 将每一行读取为一个新数组
因此对于 1 部分,您可以使用 mapfile
内置 bash 命令将您拥有的文件的所有行读入数组。
#!/bin/bash
mapfile -t arr < file.txt
现在您将拥有每行的数组,例如echo ${arr[0]}
将打印您的第一行,echo ${arr[@]}
将打印所有行。然后您可以将每一行(步骤 2)读入另一个数组并使用它们:
#!/bin/bash
# read all lines
mapfile -t arr < file.txt
# read just line 1
mapfile -t line1 < <(echo ${arr[0]} | tr ' ' '\n')
# print first element of line 1
echo ${line1[0]}
# output
"INTEL
然后您可以将这两个步骤重构为您自己的特定步骤。
请注意,您不必手动使用第二个 mapfile
(步骤 2),您可以使用 for-loop
来读取第一个数组(行数组),例如
for line in ${arr[@]}; do
# read each line into an new array
mapfile -t each_line < <(echo ${line[@]} | tr ' ' '\n');
# do whatever you need to do with each line
echo "each line: ${each_line[@]}";
done
另外您应该删除文件中的额外字符,例如"
、 ,
等,然后再使用内容/值。
仅使用您显示的示例,请尝试以下操作。
awk '
match($0,/".* /){
val1=""
val=substr($0,RSTART+1,RLENGTH-1)
sub(/[[:space:]]+$/,"",val)
num=split(val,arr," ")
for(i=2;i<=num;i++){
val1=(val1?val1 OFS:"")arr[i]
}
printf("node_nvme_device{manufacturer=\"%s\",partnumber=\"%s\",serialnumber=\"%s\"} %01d\n",arr[1],val1,$(NF-1),$NF)
}' Input_file
说明: 简单地使用awk
的匹配函数按照显示的样本从开始到更大的空间进行匹配,然后将其匹配的子字符串保存到val中,删除空格以摆脱它,然后创建 val2 ,它具有从第二个元素到数组末尾的所有值(因为 OP eg--> SSD 970 PRO 1TB
在此处匹配的样本中有多个值),然后最后使用 printf 根据需要的输出打印值,只需传递值(当前行的字段和数组元素的 val1 值在这里)。
这是另一种 awk 替代方案:
awk '
{
mf = $1
sn = $(NF-1)
gsub(/^[[:blank:]]*"[^[:blank:]]+[[:blank:]]+|[[:blank:]]{2,}[[:alnum:]].*$/,"")
printf "node_nvme_device{manufacturer=%s\",serialnumber=\"%s\"} 1\n",mf,$0,sn
}' file
node_nvme_device{manufacturer="INTEL",partnumber="SSDPEL1D380GA",serialnumber="CCCCCCCCCCCCCCC"} 1
node_nvme_device{manufacturer="Samsung",partnumber="SSD 970 PRO 1TB",serialnumber="XXXXXXXXXXXXXXX"} 1
node_nvme_device{manufacturer="Samsung",serialnumber="YYYYYYYYYYYYYYY"} 1
node_nvme_device{manufacturer="Samsung",serialnumber="ZZZZZZZZZZZZZZZ"} 1
node_nvme_device{manufacturer="Samsung",serialnumber="IIIIIIIIIIIIIII"} 1
,
假设数据以制表符分隔并使用 Python:
#!/usr/bin/python
for f in open("parts"): # Open a file called parts
f=f.replace("\"","") # Remove quotes from the line
f=f.replace(",","") # Remove commas from the line
bits=f.split("\t") # Split the line into the array bits based on tabs
bits1=bits[0].split(" ") # Split the first index further into bits1 this time using " "
stryng=bits1[0]
numb=int(bits[2])
for i in range(1,len(bits1),1): # Loop through bit1,building a strinf stryng
stryng=stryng + " " + bits1[i]
print("node_nvme_device{manufacturer=\"" + bits1[0] + "\",partnumber=\"" + stryng + "\",serialnumber=\"" + bits[1] + "\"} " + str(numb)) # Print the data in format required