您如何使用 grep 在文件中查找模式,使用 awk(或其他东西)编辑它,然后保存它?
How do you use grep to find a pattern in a file, EDIT IT with awk (or some other thing), and then save it?
我需要编辑文本文件中的特定行。我这里有一个模式,pattern.txt
:
1
3
6
17
etc...
和一个包含文本的文件,file.txt
:
1 text
2 text
3 text
4 text
5 text
etc...
我想将 _PUT FLAG HERE
添加到 file.txt
的每一行的末尾,这些行与 pattern.txt
指示的匹配。
我有
grep -F -f pattern.txt file.txt | awk '{print [=12=] "_PUT FLAG HERE" }'
但我似乎无法找到一种方法将这些更改推回到原始文件中,因此它看起来像这样:
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
6 teeskjtkljeltsj _PUT FLAG HERE
etc...
这很像尝试使用 tr
,但要复杂得多。应该有一种合乎逻辑的方法来串接 AWK 和 grep,我似乎无法想出一种方法来将这些部分放在一个管道中来执行此操作,而且我在任何地方都找不到答案。 (如果您解释了执行此操作的 sed 方法,请解释正则表达式。)
awk
救援!
您不需要其他具有 awk 全部功能的工具供您使用
$ awk -v tag='_PUT FLAG HERE' 'NR==FNR{a[];next}
{print [=10=] ( in a?tag:"")}' pattern file
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
作为练习,用join/sort
做同样的事情
$ sort <(join pattern file --nocheck-order |
sed 's/$/_PUT_FLAG_HERE/') <(join -v2 pattern file --nocheck-order)
1 text_PUT_FLAG_HERE
2 text
3 text_PUT_FLAG_HERE
4 text
5 text
也许为 DRY
定义函数
$ f() { join pattern file --nocheck-order; }; sort <(f "" |
sed 's/$/_PUT_FLAG_HERE/') <(f -v2)
假设你的 awk 被劫持了。
GNU sed/grep 解决方案!要生成执行所需操作的 sed 脚本,我们从输入文件中获取要更改的行:
$ grep -wFf pattern.txt file.txt
1 text
3 text
6 text
17 text
这会匹配完整的单词 (-w
),因此匹配 1 text
,但不匹配 11 text
; -F
用于固定字符串(没有正则表达式,应该更快)并且 -f pattern.txt
从文件中读取要查找的模式。
现在我们将其通过管道传输到 sed 以生成脚本:
$ grep -wFf pattern.txt file.txt | sed 's#.*#/^&$/s/$/_PUT FLAG HERE/#'
/^1 text$/s/$/_PUT FLAG HERE/
/^3 text$/s/$/_PUT FLAG HERE/
/^6 text$/s/$/_PUT FLAG HERE/
/^17 text$/s/$/_PUT FLAG HERE/
管道中的sed命令匹配完整的行(.*
)并组装一个地址加替换命令(&
代表之前匹配的整个行)。
现在我们采用所有这些并通过进程替换将其用作 sed 的输入(需要 Bash):
$ sed -f <(grep -wFf pattern.txt file.txt | sed 's#.*#/^&$/s/$/_PUT FLAG HERE/#') file.txt
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
6 text_PUT FLAG HERE
7 text
8 text
9 text
10 text
11 text
12 text
13 text
14 text
15 text
16 text
17 text_PUT FLAG HERE
完成!
对对对,awk更短1,更快更漂亮
1其实不是,但还是。
另注:实际上不需要 grep 步骤,请参阅 potong 和 Walter A 的回答。
试试这个:
pattern.txt:
1
3
6
17
file.txt:
1 text
2 text
3 text
4 text
5 text
使用 awk:
$ awk 'NR == FNR{seen[];next} in seen{printf("%s_PUT FLAG HERE\n",[=12=]);next}1' pattern.txt file.txt
输出:
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
@Benjamin的解可以简化为
sed -f <(sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt) file.txt
说明
# Read awk commands from a file
sed -f awkcommands.txt pattern.txt file.txt
# Read awk commands from other command
sed -f <(other_command) file.txt
# Append string to every line by replacing end-of-line character $
sed 's/$/_PUT FLAG HERE/'
# Only append string on lines matching something
sed '/something/s/$/_PUT FLAG HERE/#'
# Only append string on lines matching something at the beginning of the line followed by a space
sed '/^something /s/$/_PUT FLAG HERE/#'
# Get the word something in above command selecting the whole line with .* and putting it in the new sed command with &.
# The slashes are used for the inner sed command, so use # here
sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt
# Now all together:
sed -f <(sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt) file.txt
这可能对你有用 (GNU sed):
sed 's#.*#/&/s/$/_PUT FLAG HERE/#' pattern.txt | sed -f - file
这会将模式文件转换为 sed 脚本,然后针对文本文件调用该脚本。
此解决方案仅使用 Bash (4.0+) 功能:
# Set up associative array 'patterns' whose keys are patterns
declare -A patterns
for pat in $(< pattern.txt) ; do patterns[$pat]=1 ; done
# Slurp all the lines of 'file.txt' into the 'lines' array
readarray -t lines < file.txt
# Write each old line in the file, possibly with a suffix, back to the file
for line in "${lines[@]}" ; do
read -r label text <<< "$line"
printf '%s%s\n' "$line" "${patterns[$label]+_PUT FLAG HERE}"
done > file.txt
备注:
- 更改被写回 'file.txt',正如问题似乎指定的那样。
- Bash 关联数组和
readarray
. 需要 4.0 或更高版本
- Bash 非常慢,因此如果其中一个文件很大(超过 10,000 行),此解决方案可能不实用。
我需要编辑文本文件中的特定行。我这里有一个模式,pattern.txt
:
1
3
6
17
etc...
和一个包含文本的文件,file.txt
:
1 text
2 text
3 text
4 text
5 text
etc...
我想将 _PUT FLAG HERE
添加到 file.txt
的每一行的末尾,这些行与 pattern.txt
指示的匹配。
我有
grep -F -f pattern.txt file.txt | awk '{print [=12=] "_PUT FLAG HERE" }'
但我似乎无法找到一种方法将这些更改推回到原始文件中,因此它看起来像这样:
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
6 teeskjtkljeltsj _PUT FLAG HERE
etc...
这很像尝试使用 tr
,但要复杂得多。应该有一种合乎逻辑的方法来串接 AWK 和 grep,我似乎无法想出一种方法来将这些部分放在一个管道中来执行此操作,而且我在任何地方都找不到答案。 (如果您解释了执行此操作的 sed 方法,请解释正则表达式。)
awk
救援!
您不需要其他具有 awk 全部功能的工具供您使用
$ awk -v tag='_PUT FLAG HERE' 'NR==FNR{a[];next}
{print [=10=] ( in a?tag:"")}' pattern file
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
作为练习,用join/sort
做同样的事情$ sort <(join pattern file --nocheck-order |
sed 's/$/_PUT_FLAG_HERE/') <(join -v2 pattern file --nocheck-order)
1 text_PUT_FLAG_HERE
2 text
3 text_PUT_FLAG_HERE
4 text
5 text
也许为 DRY
定义函数$ f() { join pattern file --nocheck-order; }; sort <(f "" |
sed 's/$/_PUT_FLAG_HERE/') <(f -v2)
假设你的 awk 被劫持了。
GNU sed/grep 解决方案!要生成执行所需操作的 sed 脚本,我们从输入文件中获取要更改的行:
$ grep -wFf pattern.txt file.txt
1 text
3 text
6 text
17 text
这会匹配完整的单词 (-w
),因此匹配 1 text
,但不匹配 11 text
; -F
用于固定字符串(没有正则表达式,应该更快)并且 -f pattern.txt
从文件中读取要查找的模式。
现在我们将其通过管道传输到 sed 以生成脚本:
$ grep -wFf pattern.txt file.txt | sed 's#.*#/^&$/s/$/_PUT FLAG HERE/#'
/^1 text$/s/$/_PUT FLAG HERE/
/^3 text$/s/$/_PUT FLAG HERE/
/^6 text$/s/$/_PUT FLAG HERE/
/^17 text$/s/$/_PUT FLAG HERE/
管道中的sed命令匹配完整的行(.*
)并组装一个地址加替换命令(&
代表之前匹配的整个行)。
现在我们采用所有这些并通过进程替换将其用作 sed 的输入(需要 Bash):
$ sed -f <(grep -wFf pattern.txt file.txt | sed 's#.*#/^&$/s/$/_PUT FLAG HERE/#') file.txt
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
6 text_PUT FLAG HERE
7 text
8 text
9 text
10 text
11 text
12 text
13 text
14 text
15 text
16 text
17 text_PUT FLAG HERE
完成!
对对对,awk更短1,更快更漂亮
1其实不是,但还是。
另注:实际上不需要 grep 步骤,请参阅 potong 和 Walter A 的回答。
试试这个:
pattern.txt:
1
3
6
17
file.txt:
1 text
2 text
3 text
4 text
5 text
使用 awk:
$ awk 'NR == FNR{seen[];next} in seen{printf("%s_PUT FLAG HERE\n",[=12=]);next}1' pattern.txt file.txt
输出:
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
@Benjamin的解可以简化为
sed -f <(sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt) file.txt
说明
# Read awk commands from a file
sed -f awkcommands.txt pattern.txt file.txt
# Read awk commands from other command
sed -f <(other_command) file.txt
# Append string to every line by replacing end-of-line character $
sed 's/$/_PUT FLAG HERE/'
# Only append string on lines matching something
sed '/something/s/$/_PUT FLAG HERE/#'
# Only append string on lines matching something at the beginning of the line followed by a space
sed '/^something /s/$/_PUT FLAG HERE/#'
# Get the word something in above command selecting the whole line with .* and putting it in the new sed command with &.
# The slashes are used for the inner sed command, so use # here
sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt
# Now all together:
sed -f <(sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt) file.txt
这可能对你有用 (GNU sed):
sed 's#.*#/&/s/$/_PUT FLAG HERE/#' pattern.txt | sed -f - file
这会将模式文件转换为 sed 脚本,然后针对文本文件调用该脚本。
此解决方案仅使用 Bash (4.0+) 功能:
# Set up associative array 'patterns' whose keys are patterns
declare -A patterns
for pat in $(< pattern.txt) ; do patterns[$pat]=1 ; done
# Slurp all the lines of 'file.txt' into the 'lines' array
readarray -t lines < file.txt
# Write each old line in the file, possibly with a suffix, back to the file
for line in "${lines[@]}" ; do
read -r label text <<< "$line"
printf '%s%s\n' "$line" "${patterns[$label]+_PUT FLAG HERE}"
done > file.txt
备注:
- 更改被写回 'file.txt',正如问题似乎指定的那样。
- Bash 关联数组和
readarray
. 需要 4.0 或更高版本
- Bash 非常慢,因此如果其中一个文件很大(超过 10,000 行),此解决方案可能不实用。