使用文本文件每一行的修改版本作为 sed 命令中的参数 (bash)
use a modified version of each line of a text file as argument in a sed command (bash)
我需要从 file1 中提取两个字符串之间的所有文本。第一个字符串是 file2 的每一行,第二个字符串始终是“Lambda”。但是,我不知道如何在我的 sed 命令中说明 file2 的每个字符串。此外,我需要在 file2 的每一行的开头删除一个“>”,以匹配 file1 的内容:
示例文件 1:
some_text1
random text
Lambda
some_text2
random text
Lambda
some_text3
random text
Lambda
示例文件 2:
>some_text1
>some_text3
我想出了这个不完整的 1 行命令:
sed -n '/**line from file2, without ">" at the beginning**/,/^Lambda/p' file1
虽然不完整,但这是我的循环想法(这不包括删除 >,我在命令中也需要它):
for line in file1; do sed -n '/$line/,/^Lambda/p' file1; done
示例输出(请注意 some_text2 不存在,因为它不在文件 2 上:
some_text1
random text
Lambda
some_text3
random text
Lambda
我能做什么?
运行 的多个副本 sed
因为这是非常低效的。下面是一个awk脚本,只需要读取一次file1
,不管file2
有多长:
#!/usr/bin/env bash
awk '
BEGIN { in_block=0 }
NR==FNR { array[substr([=10=], 2)]=1; next }
in_block == 0 {
for (item in array) {
if ([=10=] ~ item) {
in_block=1
print([=10=])
next
}
}
}
in_block == 1 { print }
in_block == 1 && /^Lambda/ { in_block=0 }
' file2 file1
您可以使用 sed
通过创建一个匹配 file2 中所有字符串的单一模式,然后 运行 它只在 file1 上一次来更有效地做到这一点。对于您的示例,模式类似于 (some_text1|some_text3)
(尽管这是“扩展”正则表达式语法,因此您需要使用 sed -E
)。像这样:
lines=$(sed -n 's/^>//p' file2) # This just reads in the lines with > removed
pattern="(${lines//$'\n'/|})" # This actually converts them to a regex pattern
sed -En "/${pattern}/,/^Lambda/ p" file1 # Extract all matching ranges
请注意,如果您希望 file2 中的字符串匹配整行,而不仅仅是行中的某处,您可以使用:
pattern="^(${lines//$'\n'/|})$" # The ^ and $ anchor to the beginning & end of line
此外,请注意,如果 file2 中的行包含任何正则表达式元字符,它们将被视为它们的正则表达式含义;如果您希望将它们视为严格的文字字符串,则需要对它们进行预处理以转义 shell 元字符。如果它们包含 /
,那也需要转义。
在循环中使用 sed
主要是不好的做法。您可以考虑使用下面的版本,它首先创建 sed
命令(使用 sed
本身!),然后调用 sed
来处理这些命令:
sed -n -f <(sed \
-e 's/.//' \
-e 's/[]\/*.[]/\&/g' \
-e 's%.*%/^&$/,/^Lambda$/p%' file2) file1
如果可以保证 file2 不包含任何 []\/*.
个字符,您可能希望省略 -e 's/[]\/*.[]/\&/g'
部分。注意<(...)
表达式是process substitution in bash
;它显示为包含括号之间命令输出的文件。
尝试
{mawk/mawk2/gawk} 'BEGIN { FS = "^[>]"; FN = ARGV[--ARGC];
while (getline < FN) { lookupL[]++ };
close(FN);
FN = ARGV[ARGC] = "";
FS = "^Lambda";
} {
match([=10=], /[[:graph:]]+/);
if (substr([=10=], RSTART, RLENGTH) in lookupL) {
do { print;
if (NF>1) {break}
} while (getline);
}
}' file1 file2
file2 的大小应该不是什么大问题,除非你说的超过 2 GB。
我需要从 file1 中提取两个字符串之间的所有文本。第一个字符串是 file2 的每一行,第二个字符串始终是“Lambda”。但是,我不知道如何在我的 sed 命令中说明 file2 的每个字符串。此外,我需要在 file2 的每一行的开头删除一个“>”,以匹配 file1 的内容:
示例文件 1:
some_text1
random text
Lambda
some_text2
random text
Lambda
some_text3
random text
Lambda
示例文件 2:
>some_text1
>some_text3
我想出了这个不完整的 1 行命令:
sed -n '/**line from file2, without ">" at the beginning**/,/^Lambda/p' file1
虽然不完整,但这是我的循环想法(这不包括删除 >,我在命令中也需要它):
for line in file1; do sed -n '/$line/,/^Lambda/p' file1; done
示例输出(请注意 some_text2 不存在,因为它不在文件 2 上:
some_text1
random text
Lambda
some_text3
random text
Lambda
我能做什么?
运行 的多个副本 sed
因为这是非常低效的。下面是一个awk脚本,只需要读取一次file1
,不管file2
有多长:
#!/usr/bin/env bash
awk '
BEGIN { in_block=0 }
NR==FNR { array[substr([=10=], 2)]=1; next }
in_block == 0 {
for (item in array) {
if ([=10=] ~ item) {
in_block=1
print([=10=])
next
}
}
}
in_block == 1 { print }
in_block == 1 && /^Lambda/ { in_block=0 }
' file2 file1
您可以使用 sed
通过创建一个匹配 file2 中所有字符串的单一模式,然后 运行 它只在 file1 上一次来更有效地做到这一点。对于您的示例,模式类似于 (some_text1|some_text3)
(尽管这是“扩展”正则表达式语法,因此您需要使用 sed -E
)。像这样:
lines=$(sed -n 's/^>//p' file2) # This just reads in the lines with > removed
pattern="(${lines//$'\n'/|})" # This actually converts them to a regex pattern
sed -En "/${pattern}/,/^Lambda/ p" file1 # Extract all matching ranges
请注意,如果您希望 file2 中的字符串匹配整行,而不仅仅是行中的某处,您可以使用:
pattern="^(${lines//$'\n'/|})$" # The ^ and $ anchor to the beginning & end of line
此外,请注意,如果 file2 中的行包含任何正则表达式元字符,它们将被视为它们的正则表达式含义;如果您希望将它们视为严格的文字字符串,则需要对它们进行预处理以转义 shell 元字符。如果它们包含 /
,那也需要转义。
在循环中使用 sed
主要是不好的做法。您可以考虑使用下面的版本,它首先创建 sed
命令(使用 sed
本身!),然后调用 sed
来处理这些命令:
sed -n -f <(sed \
-e 's/.//' \
-e 's/[]\/*.[]/\&/g' \
-e 's%.*%/^&$/,/^Lambda$/p%' file2) file1
如果可以保证 file2 不包含任何 []\/*.
个字符,您可能希望省略 -e 's/[]\/*.[]/\&/g'
部分。注意<(...)
表达式是process substitution in bash
;它显示为包含括号之间命令输出的文件。
尝试
{mawk/mawk2/gawk} 'BEGIN { FS = "^[>]"; FN = ARGV[--ARGC];
while (getline < FN) { lookupL[]++ };
close(FN);
FN = ARGV[ARGC] = "";
FS = "^Lambda";
} {
match([=10=], /[[:graph:]]+/);
if (substr([=10=], RSTART, RLENGTH) in lookupL) {
do { print;
if (NF>1) {break}
} while (getline);
}
}' file1 file2
file2 的大小应该不是什么大问题,除非你说的超过 2 GB。