多行多个字符串到一行

多行多个字符串到一行

输入(多行):

abc def ghi 123 345 456 
abc def def ghi 123 345 456
abc def def def ghi 123 345 456 

输出(从一行到一行提取字符串/正则表达式):

def 345
def def 345
def def def 345

第一的..

echo "abc 123" | grep -Po "\Kabc|\K123"

但这会打印两行:

abc
123

第二:

echo -ne "abc def bac 123\nabc def def bac 123\nabc def def def bac 123 123\n" | grep -Po "def|123" | paste -d ' ' - -

但这表明:

def 123
def def
123 def
def def
123 123

我想:

def 123
def def 123
def def def 123 123

我无法使用 tr 删除 \n,def 或 345 可以在一行中多次找到,然后删除每隔一行 \n 没有任何意义。我无法使用列分隔符。

答案1

perl

$ cat ip.txt
abc def ghi 123 345 456 
abc def def ghi 123 345 456
abc def def def ghi 123 345 456 1234

$ perl -lane 'print join " ", grep { /def|123/ } @F' ip.txt
def 123
def def 123
def def def 123 1234

$ perl -lane 'print join " ", grep { $_ eq "def" || $_ eq "123" } @F' ip.txt
def 123
def def 123
def def def 123
  • -lane这里-l将从输入行中删除换行符并在print使用时将其添加回来,-a将在空格上自动分割输入行并将结果保存在@F数组中,-n将循环输入行但不会在处理后自动打印行并-e允许从命令提供 Perl 脚本线
  • grep { /def|123/ } @F将过滤@F数组的所有元素,如果它们包含def123
    • 如果你想要字符串匹配而不是正则表达式,你可以使用grep { $_ eq "def" || $_ eq "123" } @F
  • print join " "打印从输出中获得的元素grep,并以空格作为分隔符

答案2

ex与以下一起使用awk

$ cat test.txt
abc def ghi 123 345 456 
abc def def ghi 123 345 456
abc def def def ghi 123 345 456
$ printf '%s\n' 'g/^/.!awk -v ORS=" " -v RS=" " "/^(def|345)$/"' %p | ex test.txt
def 345 
def def 345 
def def def 345 
$ 

其作用是:

  1. 将文件读入缓冲区(在 中ex),可以在其中修改、打印和/或保存文件;
  2. awk通过脚本(单独)过滤缓冲区的每一行;
  3. 打印缓冲区的全部内容(带有%p)。

上述命令不会将结果保存回文件中。如果您想这样做,只需将 替换%px.


更长的解释:

ex是可编写脚本的文件编辑器。它接受文件名 ( test.txt) 作为参数,并从其标准输入获取编辑命令。

这里我们提供使用的编辑命令printf。第一个参数printf是格式化字符串,在本例中是'%s\n',它用于控制其余参数的printf输出方式。我们说所有参数都是字符串,并且每个参数后面都应该打印一个换行符。 (单引号是为了避免让 shell 解释反斜杠——我们想要printf得到反斜杠,而不是 shell。)

ex我们发送给using两个参数printf。他们来了:

g/^/.!awk -v ORS=" " -v RS=" " "/^(def|345)$/"
%p

其中第二个是最简单的。 %是地址范围;它的意思是“整个缓冲区”。 p是打印命令。所以这只是意味着“打印整个缓冲区”。

第一个需要一些分解。

g/.../是“全局”命令。它在整个缓冲区中搜索与给定模式匹配的行(在本例中^,正则表达式表示“行的开头”),并ex在每个这样的行上运行以下编辑命令。由于每一行都有一个行开头,因此每一行都匹配^,因此效果是在每一行上分别运行以下命令。

然后.是一个地址,意思是“(缓冲区的)当前行”。由于它是在g命令之后给出的,因此它依次引用缓冲区的每一行。

!用于运行 shell 命令。当它以地址为前缀时(在本例中.),给定的行范围(或单行)将被馈送到给定的 shell 命令标准输入命令的结果(标准输出)被放置在缓冲区的该行的位置。

换句话说,.!shell-command-herein是ex指通过一些外部命令来过滤缓冲区的当前行。

我们已经介绍了此命令设置如何通过命令(单独)过滤缓冲区的每一行awk;现在让我们分析一下该awk命令:

awk -v ORS=" " -v RS=" " "/^(def|345)$/"

awk您可以使用该标志定义变量-v。因此,前几个参数将ORSRS变量设置为单个空格字符。

RSinawk是“记录分隔符”;默认情况下它的值是换行符。无论它设置为什么字符,都awk将在读入记录时用于分隔记录(通常是行)。

类似地,ORS“输出记录分隔符”控制awk打印输出时用于分隔记录(通常是行)的内容。

通过将每个单词设置为空格字符,我们可以轻松地将行中的每个单词作为单个记录进行操作。

下一部分是实际的awk命令。 (awk是它自己的脚本语言。) awk命令块由条件和动作组成;任一都可以省略。这里,条件是/.../哪个是正则表达式匹配,即该条件适用于与给定正则表达式匹配的所有记录(在本例中为单词)。正则表达式部分是^(字符串开头)、$(字符串结尾)以及括号中分组的两个可能模式,并用|(竖线)分隔以指示这些模式中的任何一个都是可接受的。

由于条件后没有任何操作(操作将位于大括号中awk),awk 的默认操作“print”将应用于与该条件匹配的记录。 (请记住,这意味着awk将打印该行的每个匹配记录(单词),然后读取该输出并将其放在首先输入的ex缓冲区的行的位置。)exawk

该解决方案确实做出了简化的假设,即所有模式都将与完整的单词进行匹配,即您不希望匹配任何以下模式:包括空白。这与您在问题中给出的示例输入相匹配。

答案3

您可以使用awk并仅保留您想要的字段:

echo -e "abc def bac 123\nabc def def bac 123\nabc def def def bac 123 123" \
  | awk -v var1="def" -v var2="123" '{
  i=0
  for (j=1; j<=NF; j++){
    if ($j==var1 || $j==var2){ $++i=$j }
    if (i!=j){ $j="" }
  }
  print
}'

这将循环遍历 for 循环中的字段,并将defor重新分配123给下一个字段$++i=$j(从索引 0 开始,因此第一个字段是 1,下一个字段是 2...),如果索引为空,则将当前字段重置$j为空字符串 ( $j="")i不是循环索引j

输出:

def 123
def def 123
def def def 123 123

相关内容