用顺序索引替换字符串

use*_*338 10 shell sed awk perl text-processing

有人可以提出一种优雅的方法来实现这一目标吗?

输入:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines
Run Code Online (Sandbox Code Playgroud)

输出应该是:

test      instant1  ()

test      instant2  ()

test      instant1000()
Run Code Online (Sandbox Code Playgroud)

空行在我的输入文件中,同一目录下有许多文件需要同时处理。

我试图用这个来替换同一个目录中的许多文件,但没有用。

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines
Run Code Online (Sandbox Code Playgroud)

错误:

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.
Run Code Online (Sandbox Code Playgroud)

我也试过这个:

test      instant1  ()

test      instant2  ()

test      instant1000()
Run Code Online (Sandbox Code Playgroud)

它起作用了,但索引只是从一个文件增加到另一个文件。我想在更改为新文件时将其重置为 1。有什么好的建议吗?

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done
Run Code Online (Sandbox Code Playgroud)

有效,但它替换了不应替换的所有其他文件。我更喜欢只用*.txtonly替换文件。

Sté*_*las 14

perl -pe 's/instant/$& . ++$n/ge'
Run Code Online (Sandbox Code Playgroud)

或使用 GNU awk

awk -vRS=instant '{$0=n$0;ORS=RT}++n'
Run Code Online (Sandbox Code Playgroud)

要就地编辑文件,请将-i选项添加到perl

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*.vs
Run Code Online (Sandbox Code Playgroud)

或者递归:

find . -name '*.vs' -type f -exec perl -pi -e '
  s/instant/$& . ++$n{$ARGV}/ge' {} +
Run Code Online (Sandbox Code Playgroud)

说明

perl -pe 's/instant/$& . ++$n/ge'
Run Code Online (Sandbox Code Playgroud)

-p是逐行处理输入,评估传递给-e每一行的表达式并打印它。对于每一行,我们用(使用s/re/repl/flags运算符)instant代替它本身 ( $&) 和一个变量的递增值++$n。该g标志是进行全局替换(不仅仅是一次),e以便将替换解释为 perl 代码到e? 评估(不是固定字符串)。

对于一个 perl 调用处理多个文件的就地编辑,我们希望$n在每个文件处重置。相反,我们使用$n{$ARGV}$ARGV当前处理的文件在哪里)。

awk一个值得一些解释。

awk -vRS=instant '{$0=n$0;ORS=RT}++n'
Run Code Online (Sandbox Code Playgroud)

我们正在使用 GNU 的能力awk来分隔任意字符串(甚至正则表达式)上的记录。使用-vRS=instant,我们将r?ecord s?eparator 设置instantRT是保存匹配内容的变量RS,所以通常,instant除了最后一条记录,它将是空字符串。在上面的输入中,记录 ( $0) 和记录终止符 ( RT) 是 ( [$0|RT]):

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]
Run Code Online (Sandbox Code Playgroud)

所以我们需要做的就是在除第一条记录之外的每条记录的开头插入一个递增的数字。

这就是我们上面所做的。对于第一条记录,n将是空的。我们将 ORS(o?utput r?ecord s?eparator)设置为 RT,以便awk 打印n $0 RT。它根据第二个表达式 ( ++n) 执行此操作,这是一个始终评估为真(非零数字)的条件,因此$0 ORS对每个记录执行默认操作(打印)。

  • [这可能需要一些解释](http://chat.stackexchange.com/transcript/message/14005518#14005518)。 (4认同)

ter*_*don 5

sed确实不是这项工作的最佳工具,您需要具有更好脚本功能的工具。这里有一些选择:

  • perl

    perl -00pe 's/instant/$& . $./e' file 
    
    Run Code Online (Sandbox Code Playgroud)

    -p方法将任何脚本与给定后进行打印“每一行” -e。在-00为“段落模式”这样的记录(行)转由连续的换行定义的(\n)人物,这让它对付双行距正确。$&是最后匹配的模式,$.是输入文件的当前行号。将es///e允许我以评估替换操作符表达式。

  • awk(假设您的数据与显示的完全一样,具有三个空格分隔的字段)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 
    
    Run Code Online (Sandbox Code Playgroud)

    在这里,我们仅在当前行不为空时才增加k变量,在这种情况下,我们还会打印必要的信息。空行按原样打印。k/./

  • 各种贝壳

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 
    
    Run Code Online (Sandbox Code Playgroud)

    在这里,每个输入行都自动在空白处拆分,并且字段保存为$a,$b$c。然后,在循环中,$c对于$a不为空的每一行增加 1,并且它的当前值打印在第二个字段旁边$b

注意:上述所有解决方案都假定文件中的所有行都具有相同的格式。如果没有,@Stephane 的答案就是要走的路。


为了处理许多文件,并假设您想对当前目录中的所有文件执行此操作,您可以使用以下命令:

perl -00pe 's/instant/$& . $./e' file 
Run Code Online (Sandbox Code Playgroud)

小心:假设没有空格的简单文件名,如果需要处理更复杂的事情,请使用(假设ksh93zshbash):

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -00pe 's/instant/$& . $./e' "$file"
done
Run Code Online (Sandbox Code Playgroud)