我正在嘗試撰寫一個簡單的組態檔決議器,例如,可能有一個名為INCAR:
NSW = 1000
POTIM = 1
TEBEG = 300
如果我想提取 的值POTIM,我可以采用 awk 提取空格之間的文本并使用以下腳本:
#!/bin/bash
vaspT ()
{
if [ -f INCAR ]; then
local potim=$(grep POTIM INCAR | awk '{print $3}');
else
local potim=1;
fi;
echo "# Time step: ${potim}" > .vasp_md.dat;
echo "# Step Temperature Total_energy E_pot E_kin" >> .vasp_md.dat;
}
vaspT
但是如果有人不遵循對齊規則,請使用如下組態檔:
NSW = 1000
POTIM=1
TEBEG=300
然后,我必須使用另一個分隔符。
我的問題是:
這種作業是否有簡單的解決方案或現有的庫(可以接受 Python 或 Bash)?
uj5u.com熱心網友回復:
在這種情況下,您可以使用正則運算式:
import re
myString = """
NSW = 1000
POTIM = 1
TEBEG=300
"""
re.findall("POTIM(\s )?\=(\s )?(\d )", myString)
輸出
[(' ', ' ', '1')]
如果你在這個模式中使用正則運算式,無論有多少空格,元組的最后一個元素(如果有的話)總是你想要的變數。
另一個例子
import re
myString = """
NSW = 1000
POTIM=1
TEBEG=300
"""
re.findall("POTIM(\s )?\=(\s )?(\d )", myString)
輸出
[('', '', '1')]
uj5u.com熱心網友回復:
我將cut用于這個用例......
grep POTIM INCAR | cut -d "=" -f 2 | sed s/\ //g
cut -d "=" -f 2將采用關于=分隔符的第二個欄位。sed s/\ //g將洗掉值周圍的空格
uj5u.com熱心網友回復:
您可以使用 awk 并將欄位分隔符設定為=可選空格之間。
如果第一個欄位是 POTIM,則列印第二個欄位。
awk -F"[[:space:]]*=[[:space:]]*" '
$1=="POTIM" {print $2}
' file
輸出
1
uj5u.com熱心網友回復:
您可以創建一個包含變數及其值的字典:
import re
with open("filename", "r") as f:
config = dict(re.findall(r"(\w )\s*=\s*(\w )", f.read()))
print(config)
輸出:
{'NSW': '1000', 'POTIM': '1', 'TEBEG': '300'}
然后,您可以輕松地檢索每個變數的值:
print(config["POTIM"]) # 1
(\w )\s*=\s*(\w )
(\w ):第一個捕獲組,匹配任意單詞字符 1 次到無限次。\s*: 匹配 0 到無限次之間的任何空格。=: 匹配=。\s*: 匹配 0 到無限次之間的任何空格。(\w ): 第二個捕獲組,匹配任意單詞字符 1 次到無限次。
對于每個匹配,re.findall將創建一個包含捕獲組的元組。然后使用dict()會將串列轉換為字典。
uj5u.com熱心網友回復:
使用sed
#!/bin/bash
vaspT ()
{
if [ -f INCAR ]; then
local potim
potim=$(sed -n '/POTIM/s/.*=[[:space:]]\?\(.*\)/\1/p' INCAR)
else
local potim
potim=1
fi
echo "# Time step: ${potim}" > .vasp_md.dat
echo "# Step Temperature Total_energy E_pot E_kin" >> .vasp_md.dat
}
vaspT
uj5u.com熱心網友回復:
對于這種作業,是否有簡單的解決方案或現有的庫(...)Python(...)?
在configparserpython標準庫中有,但它確實假設總是有標題,所以如果你的檔案沒有,你需要添加一個,考慮下面的例子,讓file.txt內容
ZERO=0
LEFT =1
RIGHT= 1
BOTH = 2
MULTI = 3
那么它可以按如下方式使用
import configparser
config = configparser.ConfigParser()
with open("file.txt","r") as f:
config.read_string('[default]\n' f.read())
print(config['default']['ZERO']) # 0
print(config['default']['LEFT']) # 1
print(config['default']['RIGHT']) # 1
print(config['default']['BOTH']) # 2
print(config['default']['MULTI']) # 3
說明:我添加默認行以允許configparser作業。請注意,此解決方法,您可能會選擇強制用戶使用標頭而不是使用此解決方法,在這種情況下使用變得更容易:
import configparser
config = configparser.ConfigParser()
config.read("file.txt")
...
uj5u.com熱心網友回復:
你在 shell 中做的太多了。awk 是發明 shell 的人也發明了用于呼叫 shell 來操作文本的工具,因此只需使用 awk 進行整個文本操作,而不是不必要地添加其他 shell 命令來一次輸入 awk 一行等。
您的問題沒有告訴我們如果檔案存在但不包含 POTIM= 行或包含多個 POTIM= 行或如何處理檔案中的注釋(或這些注釋的樣子)該怎么辦,因此忽略了評論并猜測如果 POTIM= 不存在你想列印 1 而如果它確實存在你想列印最后看到的值:
$ cat tst.sh
#!/usr/bin/env bash
vaspT() {
local infile='INCAR'
[[ -f "$infile" ]] || infile='/dev/null'
awk '
{
gsub(/^[[:space:]] |[[:space:]] $/,"")
tag = val = $0
sub(/[[:space:]]*=.*/,"",tag)
sub(/[^=]*=[[:space:]]*/,"",val)
tag2val[tag] = val
}
END {
print "# Time step:", ("POTIM" in tag2val ? tag2val["POTIM"] : 1)
print "# Step Temperature Total_energy E_pot E_kin"
}
' "$infile" > .vasp_md.dat
}
vaspT
$ ./tst.sh
$ cat .vasp_md.dat
# Time step: 1
# Step Temperature Total_energy E_pot E_kin
我用這個:
{
gsub(/^[[:space:]] |[[:space:]] $/,"")
tag = val = $0
sub(/[[:space:]]*=.*/,"",tag)
sub(/[^=]*=[[:space:]]*/,"",val)
tag2val[tag] = val
}
而不僅僅是:
BEGIN { FS = "[[:space:]]*=[[:space:]]*" }
{ tag2val[$1] = $2 }
因此,如果行上有前導或尾隨空格或包含 的值,則代碼將繼續作業=,例如:
NSW = 1000
POTIM = "foo=bar"
TEBEG = 300
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/451496.html
上一篇:用戶輸入變數并grep模式檔案
下一篇:數字作為命令列引數并列印計數
