输入(多行):
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
数组的所有元素,如果它们包含def
或123
- 如果你想要字符串匹配而不是正则表达式,你可以使用
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
$
其作用是:
- 将文件读入缓冲区(在 中
ex
),可以在其中修改、打印和/或保存文件; awk
通过脚本(单独)过滤缓冲区的每一行;- 打印缓冲区的全部内容(带有
%p
)。
上述命令不会将结果保存回文件中。如果您想这样做,只需将 替换%p
为x
.
更长的解释:
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-here
in是ex
指通过一些外部命令来过滤缓冲区的当前行。
我们已经介绍了此命令设置如何通过命令(单独)过滤缓冲区的每一行awk
;现在让我们分析一下该awk
命令:
awk -v ORS=" " -v RS=" " "/^(def|345)$/"
awk
您可以使用该标志定义变量-v
。因此,前几个参数将ORS
和RS
变量设置为单个空格字符。
RS
inawk
是“记录分隔符”;默认情况下它的值是换行符。无论它设置为什么字符,都awk
将在读入记录时用于分隔记录(通常是行)。
类似地,ORS
“输出记录分隔符”控制awk
打印输出时用于分隔记录(通常是行)的内容。
通过将每个单词设置为空格字符,我们可以轻松地将行中的每个单词作为单个记录进行操作。
下一部分是实际的awk
命令。 (awk
是它自己的脚本语言。) awk
命令块由条件和动作组成;任一都可以省略。这里,条件是/.../
哪个是正则表达式匹配,即该条件适用于与给定正则表达式匹配的所有记录(在本例中为单词)。正则表达式部分是^
(字符串开头)、$
(字符串结尾)以及括号中分组的两个可能模式,并用|
(竖线)分隔以指示这些模式中的任何一个都是可接受的。
由于条件后没有任何操作(操作将位于大括号中awk
),awk 的默认操作“print”将应用于与该条件匹配的记录。 (请记住,这意味着awk
将打印该行的每个匹配记录(单词),然后读取该输出并将其放在首先输入的ex
缓冲区的行的位置。)ex
awk
该解决方案确实做出了简化的假设,即所有模式都将与完整的单词进行匹配,即您不希望匹配任何以下模式:包括空白。这与您在问题中给出的示例输入相匹配。
答案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 循环中的字段,并将def
or重新分配123
给下一个字段$++i=$j
(从索引 0 开始,因此第一个字段是 1,下一个字段是 2...),如果索引为空,则将当前字段重置$j
为空字符串 ( $j=""
)i
不是循环索引j
。
输出:
def 123
def def 123
def def def 123 123