仅从给定行获取位于行中间的关键字之后的值

仅从给定行获取位于行中间的关键字之后的值

嘿,所以我在文件中间有以下行,我只需要获取“energy=”后面的值。行号存储在名为“lineNumber”的变量中。文件中还有其他行具有相同的结构,但具有不同的值。我只想要“lineNumber”中定义的线的能量值。非常感谢您的帮助。谢谢你!

Properties=species:S:1:pos:R:3:velocities:R:3:forces:R:3:local_energy:R:1:fix_atoms:S:3 Lattice="42.0000000000       0.0000000000    0.0000000000    0.0000000000   46.0000000000    0.0000000000    0.0000000000    0.0000000000   50.0000000000" temperature=327.11679001 pressure=14.24003276 time_step=5.0000 time=5000.0000 energy=-18.022194 virial="0.46990039            0.48760331     -0.77576961      0.48760331      0.78141847      0.59471844     -0.77576961      0.59471844      0.64787347" stress="-0.00000486          -0.00000505      0.00000803     -0.00000505     -0.00000809     -0.00000616      0.00000803     -0.00000616     -0.00000671" volume=96600.000000 step=1000

答案1

由于您使用的是基于 Linux 的系统,因此几乎可以肯定使用 GNUgrep

grep -oP 'energy=\K[^\s]+'

例如

echo 'Properties=species:S:1:pos:R:3:velocities:R:3:forces:R:3:local_energy:R:1:fix_atoms:S:3 Lattice="…" temperature=327.11679001 … time=5000.0000 energy=-18.022194 virial="0.46990039 …" stress="…" volume=96600.000000 step=1000' |
    grep -oP 'energy=\K[^\s]+'

输出

-18.022194

您可以使用诸如sed

lineNumber=123
sed -n "${lineNumber}{p;q}" file

把这些放在一起,

sed -n "${lineNumber}{p;q}" file | grep -oP 'energy=\K[^\s]+'

你也可以使用类似的东西perl

perl -e '
    $lineNumber = shift;                                 # Arg 1 is line number
    $fieldName = shift;                                  # Arg 2 is field name
    while (defined($line = <>)) {                        # Read lines from file or stdin
        next unless $. == $lineNumber;                   # Skip until required line
        chomp $line;                                     # Discard newline
        %a =                                             # Create key/value array. Read the next lines upwards
            map { split(/=/, $_, 2) }                    # 3. Split into {key,value} tuples
            grep { /=/ }                                 # 2. Only interested in assignments
            split(/(\w+=(".*?"|[^"].*?)\s+)/, $line);    # 1. Split line into « key=value » and « key="several values" » fields
        print $a{$fieldName}, "\n";                      # Print chosen field value
        exit 0
    }
' "$lineNumber" 'energy' file

答案2

awk -v lineNumber="$lineNumber" -v FS="energy=" 'NR == lineNumber {print $2}' FILE | awk '{print $1}'

答案3

对于您当前的需求来说可能有点过分,但您可以创建一个标签到值映射的数组(存储在下面的数组中f[]):

$ awk -v FPAT='[^=[:space:]]+=([^[:space:]]+|"[^"]*")' -v n="$lineNumber" '
    NR == n {
        delete f
        for (i=1; i<=NF; i++) {
            f[gensub(/=.*/,"",1,$i)] = gensub(/[^=]+=/,"",1,$i)
        }
        print f["energy"]
    }
' file
-18.022194

f[]然后您可以通过使用标签(名称)进行索引来对任何值或值组合执行任何您喜欢的操作,例如您可以编写:

awk -v FPAT='[^=[:space:]]+=([^[:space:]]+|"[^"]*")' '
    {
        delete f
        for (i=1; i<=NF; i++) {
            f[gensub(/=.*/,"",1,$i)] = gensub(/[^=]+=/,"",1,$i)
        }
    }
    (f["time"] < 6) && (f["volume"] > 8) {
        print f["temperature"], f["energy"], f["step"] / f["time_step"]
    }
' file
327.11679001 -18.022194 200

或您可能需要比较/计算/打印的任何其他内容。

上面使用 GNU awk 处理FPATgensub(),您可以对任何支持 的 POSIX awk 执行相同的操作delete array,就像大多数(如果不是全部)现在所做的那样,只需多一点代码:

$ awk -v n="$lineNumber" '
    NR == n {
        delete f
        rec = $0
        while ( match(rec,/[^=[:space:]]+=([^[:space:]]+|"[^"]*")/) ) {
            tag = val = substr(rec,RSTART,RLENGTH)
            sub(/=.*/,"",tag)
            sub(/[^=]+=/,"",val)
            f[tag] = val
            rec = substr(rec,RSTART+RLENGTH)
        }
        print f["energy"]
    }
' file
-18.022194

如果你的 awk 抱怨,delete f那么只需将该行更改为split("",f)在任何 awk 中都可以工作的行。

答案4

使用(以前称为 Perl_6)

~$ raku -ne 'put ++$ => $/ if ++$ == 1 && m/ \s energy\= <( <+ :N + [\-+.]>+  )> \s /;'   file

或者:

~$ raku -ne 'put ++$ => $0 if ++$ == 1 && m/ \s energy\=  ( <+ :N + [\-+.]>+  )  \s /;'    file

或者:

~$ raku -ne 'put ++$ => $<val> if ++$ == 1 && m/ \s energy\=  $<val>=<+ :N + [\-+.]>+   \s /;'   file

上面的 Raku 代码使用-ne非自动打印逐行标志(代码在输入文件上逐行运行)。从右到左读取,m/.../寻找以下所需值的匹配项energy=。此匹配以 Raku Regex 方言编写,<+ :N + [\-+.]>+它是一个复合字符类,由任何:NUnicode 数字和字符-, +, ., 出现一次或多次(-是在此构造中需要反斜杠转义的少数字符之一)。也可以编写此字符类<+[0..9\-+.]>+,但您会丢失与 以外的 Unicode 数字的匹配0..9

在 Raku 中计算行号的最简单方法是运行++$从 1 开始的自动递增计数器。将它们放在一起(现在从左到右阅读),第一个示例代码说“如果行号在数字上等于 1,则行号put后跟++$内置$/匹配变量,则找到所需的匹配项,并删除捕获标记之外的所有内容”if ++$ == 1&&<( ... )>。因为&&使用了,所以布尔短路。 (++$ =>如果您想要返回的只是值,请从输出中删除 )。

样本输入(OP的数据取3次):

Properties=species:S:1:pos:R:3:velocities:R:3:forces:R:3:local_energy:R:1:fix_atoms:S:3 Lattice="42.0000000000       0.0000000000    0.0000000000    0.0000000000   46.0000000000    0.0000000000    0.0000000000    0.0000000000   50.0000000000" temperature=327.11679001 pressure=14.24003276 time_step=5.0000 time=5000.0000 energy=-18.022194 virial="0.46990039            0.48760331     -0.77576961      0.48760331      0.78141847      0.59471844     -0.77576961      0.59471844      0.64787347" stress="-0.00000486          -0.00000505      0.00000803     -0.00000505     -0.00000809     -0.00000616      0.00000803     -0.00000616     -0.00000671" volume=96600.000000 step=1000
Properties=species:S:1:pos:R:3:velocities:R:3:forces:R:3:local_energy:R:1:fix_atoms:S:3 Lattice="42.0000000000       0.0000000000    0.0000000000    0.0000000000   46.0000000000    0.0000000000    0.0000000000    0.0000000000   50.0000000000" temperature=327.11679001 pressure=14.24003276 time_step=5.0000 time=5000.0000 energy=-18.022194 virial="0.46990039            0.48760331     -0.77576961      0.48760331      0.78141847      0.59471844     -0.77576961      0.59471844      0.64787347" stress="-0.00000486          -0.00000505      0.00000803     -0.00000505     -0.00000809     -0.00000616      0.00000803     -0.00000616     -0.00000671" volume=96600.000000 step=1000
Properties=species:S:1:pos:R:3:velocities:R:3:forces:R:3:local_energy:R:1:fix_atoms:S:3 Lattice="42.0000000000       0.0000000000    0.0000000000    0.0000000000   46.0000000000    0.0000000000    0.0000000000    0.0000000000   50.0000000000" temperature=327.11679001 pressure=14.24003276 time_step=5.0000 time=5000.0000 energy=-18.022194 virial="0.46990039            0.48760331     -0.77576961      0.48760331      0.78141847      0.59471844     -0.77576961      0.59471844      0.64787347" stress="-0.00000486          -0.00000505      0.00000803     -0.00000505     -0.00000809     -0.00000616      0.00000803     -0.00000616     -0.00000671" volume=96600.000000 step=1000

示例输出(制表符分隔的返回):

1   -18.022194

最后,如果您宁愿lineNumber从命令行输入而不是在一行中对其进行硬编码,则envRaku 内部可以使用环境变量作为动态%*ENV哈希变量。所以你可以执行以下操作(注意首字母env是可选的):

~$ env lineNumber="1" perl6 -ne 'put $0 if ++$ == %*ENV<lineNumber> && m/ \s energy\=  ( <+[0..9\-+.]>+ )  \s /;'   file
-18.022194

https://docs.raku.org/language/regexes
https://raku.org

相关内容