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)
有效,但它替换了不应替换的所有其他文件。我更喜欢只用*.txt
only替换文件。
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 设置为instant
。RT
是保存匹配内容的变量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
对每个记录执行默认操作(打印)。
sed
确实不是这项工作的最佳工具,您需要具有更好脚本功能的工具。这里有一些选择:
perl
perl -00pe 's/instant/$& . $./e' file
Run Code Online (Sandbox Code Playgroud)
该-p
方法将任何脚本与给定后进行打印“每一行” -e
。在-00
为“段落模式”这样的记录(行)转由连续的换行定义的(\n
)人物,这让它对付双行距正确。$&
是最后匹配的模式,$.
是输入文件的当前行号。将e
在s///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)
小心:假设没有空格的简单文件名,如果需要处理更复杂的事情,请使用(假设ksh93
,zsh
或bash
):
find . -type f -print0 | while IFS= read -r -d ''; do
perl -i -00pe 's/instant/$& . $./e' "$file"
done
Run Code Online (Sandbox Code Playgroud)