来自文件的 sed 模式并使用字符串变量替换另一个文件中的新模式

埃米利奥岛

我有几个文件,其中有以下模式:

  t=        9.90000    2    2
  t=       10.00000    1    1

现在,t 的值(例如 100.00000)以及 (2 2) 的值正在发生变化。我想像下面这样重写它:

t =   9.9 fs st=2
t =  10.0 fs st=1

现在,我有很多困难要做到这一点。在检查此链接后,我正在尝试以下内容:

for i in {99..100};do 
  printf t="%*.5f\n" 16 $(($i))e-1 > 1.out
  x=$(grep -h -f 1.out output.xyz | cut -c 25-25)
  printf t="%*.1f fs st=$x\n" 6 $i > 2.out
  grep -h -f 1.out output.xyz > 3.out
  while read pattern; do 
    while read pattern2; do 
      sed -i 's/"${pattern}"/"${pattern2}"/' output.xyz
    done < 2.out
  done < 3.out
done

这里的问题是我用要正确替换的模式创建了文件 3.out,但是当我将它读入时pattern,它会带走几个空格。

而不是存在t= 9.90000 2 2,它是t= 9.90000 2 2我想这里的主要问题是如何将正确数量的空格发送到变量patternpattern2.

我想我已经增加了过多的复杂性,但从理论上讲,应该有效......

我很感激任何帮助。

如果有人想尝试,我会将文件 output.xyz 的示例粘贴到下面。在这里,我在 90 和 100 之间循环,但最终我会在 0 到 200 之间执行 0.1 的循环。

  t=         9.90000    2    2
H      -0.036930458      0.778649616      1.520488735
C       0.027100908      0.020521063      0.815485702
H      -0.114216621     -1.115678468      1.549274509
C      -0.028047550      0.011852199     -0.815234987
H       0.117999971     -1.007943999     -1.373022932
H       0.044427848      0.883548719     -1.649093142
           6
  t=        10.00000    1    1
H      -0.038617790      0.777486447      1.520614461
C       0.027651801      0.020640376      0.817860457
H      -0.116497310     -1.116177809      1.544694024
C      -0.028248486      0.012015286     -0.816858295
H       0.118760018     -1.012065106     -1.371494658
H       0.043469061      0.885969826     -1.655114073

谢谢

贡萨尔维斯岛

戈登戴维森

我无法复制你的结果。当我尝试它时 3.out 看起来是正确的,但该sed命令不起作用,因为变量没有被单引号替换(您从中复制的答案完全错误,并且对此效果有评论)。只使用双引号,比如sed -i "s/${pattern}/${pattern2}/" output.xyz,就可以了。

但正如你所说,它过于复杂。您正在使用一种在初级脚本编写者中很常见的反模式:查找需要一项一项更改的内容,然后将更改应用于整个文件(并希望它实际上只更改那一项)。这既低效(因为它每次都处理整个文件)并且有风险(因为更改可能会以意想不到的方式应用于文件中不相关的位置)。

你实际上做了两次这样的事情,首先扫描整个文件的位置,例如“...9.90000...”需要更改为“...9.9...”(所以你扫描整个文件每个数字),然后为您找到的行创建一个替换模式并将应用于整个文件(再次处理整个文件以更改一行)。如果您以 0.1 为增量在 0 到 200 之间执行此操作,则意味着您将搜索文件 2,001 次,并最多对其进行多次编辑 - 即整个文件的 4,002 次!如果文件中实际上有那么多条目(并且每个条目后跟 6 行其他数据,如您的示例所示)... 4,002 次通过 2001*7 行,总共处理了 56,056,014 行。

如果文件中有两行具有相同的 t= 编号,这也会奇怪地失败,因为它会尝试所有重新格式化的版本替换所有匹配的行(除第一个之外的所有行都是乱码),而无需付费注意哪个替换与哪个原始。如果一个数字没有匹配项,它也会表现得很奇怪,尽管在这种情况下它不是破坏性的。

(注意:可能永远不会有多个相同的 t= 数字,在这种情况下,这种方法可能会起作用。可能。但它仍然是一种糟糕的做事方式。)

做这样的事情的更好方法是处理文件一次,使用一种方法来处理它正在处理的行所需的所有逻辑,因为它正在处理它。你可以在sed没有太多麻烦的情况下做到这一点如果数字总是保证在第一个小数位后有零,这应该有效:

sed -Ei 's/^  t= +([ 0-9]{3}[.][0-9])0000 +([0-9]) +[0-9]$/  t= \1 fs st=\2/' output.xyz

在这里,-E选项sed告诉它使用“扩展”正则表达式语法,( )模式中是“捕获组”,可以在替换字符串中用作\1(第一个)\2(第二个)。在正则表达式中,[ 0-9]{3}意思是“三个全是空格和/或数字的字符”,以及后面跟着的各种空格的+意思是“一个或多个空格”(如果您愿意,可以用正确数量的空格替换它们)通缉)。

所以基本上,它匹配整行(如果它采用需要更改的格式),捕获重要部分,并用重新格式化的版本替换该行,并在其位置捕获数据位。不符合该格式的行将不会被匹配,因此它们将保持不变。

(我也不确定输出格式是否完全正确,因为您拥有的内容不一致。输出中的“t”之前是否应该有空格?第一个“=”周围是否应该有空格? 您可能需要在我的命令中调整替换字符串。)

编辑:要转换st值,最好再次添加逻辑以一次性完成。您可以sed通过使用 4 种模式来做到这一点,一种匹配列中的“1”或“5”([15]正则表达式表示法)st并在替换中产生“0”,一种匹配“2”或“6”等。您可以-e在每条规则之前使用也可以通过将它们分开来将它们组合成一个长参数;这是-e版本(为了可读性分成单独的行):

sed -Ei \
    -e 's/^  t= +([ 0-9]{3}[.][0-9])0000 +([15]) +[0-9]$/  t= \1 fs st=0/' \
    -e 's/^  t= +([ 0-9]{3}[.][0-9])0000 +([26]) +[0-9]$/  t= \1 fs st=1/' \
    -e 's/^  t= +([ 0-9]{3}[.][0-9])0000 +([37]) +[0-9]$/  t= \1 fs st=2/' \
    -e 's/^  t= +([ 0-9]{3}[.][0-9])0000 +([48]) +[0-9]$/  t= \1 fs st=3/' \
    output.xyz

使用这种方法,您必须小心编辑规则不会链接(除非您希望它们链接)。也就是说,您不希望一条规则将st值从“4”更改为“3”,然后另一条规则将“3”更改为“2”等。在这种情况下,一旦转换了一行它不再匹配模式,所以这不会发生(另外我将规则放在一个顺序中,无论如何都可以防止链接)。

另一种选择是切换到另一个程序,例如awk. awk通常比 更强大sed,但在哲学上也有些不同:它更像是一种真正的编程语言,并且倾向于将文本行视为字段(在输入行中,$1将是“t=”,$2例如“9.90000”等),尽管您也可以使用整行(awk调用$0)。awk也知道数学,所以转换st值只是减一并减少模 4 的问题。一个缺点是大多数版本awk不支持就地编辑(如sed -i),因此您需要将输出发送到临时文件和然后用它来替换输入文件。

awk '{if ($0 ~ /  t= +[ 0-9]{3}[.][0-9]0000 +[1-8] +[0-9]$/) printf "  t=%6.1f fs st=%d\n", $2, ($3-1)%4; else print $0}' output.xyz >output.tmp &&
    mv output.tmp output.xyz

顺便说一句,你确实有所有这些文件的良好备份,对吗?以防万一出现可怕的错误?

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

循环使用sed。用一个文件中的文本替换另一个文件中的匹配模式

使用另一个文件中的模式替换文件中的字符串

SED或AWK全部替换为另一个文件中的模式

将sed字符串从一个文件替换到另一个文件时,“替换命令中的错误标志:'{”

使用sed从一个文件中添加文本以匹配另一个文件的模式

使用sed将for循环中文件中第n个出现的字符串替换为另一个字符串中的sed

Linux / Unix替换字符串中的模式并使用sed保存到新文件

复制文件中的字符串,然后使用sed覆盖另一个字符串

如何使用 sed 在文件中的特定行附加数据,其中数据来自另一个文件

如果使用awk在另一个文件中存在模式,如何替换一个文件中的字符串

使用sed使用另一个文件的内容替换文件中的文本块

使用来自单独文件的 sed

从一个文件复制内容并需要使用 sed 在另一个文件中替换

用变量替换bash的SED,该变量是存储在数组中的另一个文件的内容

sed命令将一个文件中的字符串替换为另一文件的全部内容

在文件中使用sed替换行与另一个文件

SED / AWK:将字符串添加到与另一个源文件中的正则表达式值匹配的多个文件中

如何使用sed将文件中的特定行替换为另一个文件中的内容

我想在 Bash 脚本中使用 sed 删除日志文件中“var”变量中包含的字符串模式

Sed-替换紧跟在特定模式之后的下一个字符串单词,但仅在文件中第一次出现

sed命令读取外部文件并在单个sed行中替换另一个文件

使用sed在文件中插入另一个sed命令

sed从一个文件中读取整行,并替换另一个文件中的行

使用sed查找并替换多个文件中的字符串

使用Sed查找并替换文件中的字符串

使用sed查找并替换文件中的字符串

sed替换另一个文件内容中的单词

sed从一个文件中选择值,然后在另一个文件中替换

grep一个特定的字符串,并用sed替换文件