Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 如何使用grep在文件中查找模式,用awk(或其他东西)编辑它,然后保存它?_Bash_Awk_Sed_Grep_Tr - Fatal编程技术网

Bash 如何使用grep在文件中查找模式,用awk(或其他东西)编辑它,然后保存它?

Bash 如何使用grep在文件中查找模式,用awk(或其他东西)编辑它,然后保存它?,bash,awk,sed,grep,tr,Bash,Awk,Sed,Grep,Tr,我需要编辑文本文件中的特定行。我这里有一个模式,pattern.txt: 以及包含文本的文件file.txt: 我想在file.txt的每一行末尾添加单词_putflag,这些行由pattern.txt表示匹配 我有 grep -F -f pattern.txt file.txt | awk '{print $0 "_PUT FLAG HERE" }' 但我似乎无法找到一种方法将这些更改推回到原始文件中,因此看起来是这样的: 1 text_PUT FLAG HERE 2 text 3 text

我需要编辑文本文件中的特定行。我这里有一个模式,pattern.txt:

以及包含文本的文件file.txt:

我想在file.txt的每一行末尾添加单词_putflag,这些行由pattern.txt表示匹配

我有

grep -F -f pattern.txt file.txt | awk '{print $0 "_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 to the rescue

您不需要其他具有awk全部功能的工具供您使用

$ awk -v tag='_PUT FLAG HERE' 'NR==FNR{a[$1];next}
                              {print $0 ($1 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
也许是定义干细胞的功能

$ f() { join $1 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
这匹配完整的单词-因此匹配了1个文本,但没有匹配11个文本-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命令匹配完整的行。*并汇编一个address plus substitution命令&表示之前匹配的整个行

现在,我们将所有这些作为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实际上不是,但仍然是

另一句话:grep步骤实际上并不是必需的,请参见Poton和Walter A的答案。

试试以下方法: pattern.txt:

file.txt:

使用awk:

$ awk 'NR == FNR{seen[$1];next} $1 in seen{printf("%s_PUT FLAG HERE\n",$0);next}1' pattern.txt file.txt
输出:


@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”。 关联数组和readarray需要Bash 4.0或更高版本。 Bash非常慢,因此,如果其中任何一个文件的大小超过10000行,则此解决方案可能不实用。
如果您正在使用awk,则可以停止使用grep。几乎所有grep能做的事情,awk也能做。不需要额外的管件。也就是说,您是否可以包含一些元代码来描述您所设想的应该如何工作的逻辑?我不是从你的描述中得到的。看到第三块灰色文本了吗?我需要它看起来像那样。看看pattern.txt中的数字1和3是怎样的?它告诉我我需要编辑包含数字3或1的行,并将字符串PUT_FLAG_添加到匹配模式的模式末尾。TXT切勿使用单词模式,因为它非常模糊。请编辑您的问题,将单词pattern替换为string或regexp,无论您所指的pattern在当前使用的任何位置,并将其包含在示例输入/输出部分匹配案例中,例如pattern.txt中的3和file.txt中的137,以便我们可以看到您希望如何处理部分匹配。也包括像file.txt中的99 foo3bar这样的案例,因此建议的解决方案如果没有在正确的列中找到3,就会失败。本杰明说:“让我们来谈谈这一点——作为一种评论,这不是更合适吗?”。这一点也很好——我首先尝试了一些没有你的代码的东西,并用你的部分代码优化了我的解决方案。我认为你应该得到荣誉,所以我才提到你。我不确定你会编辑你的答案,我想为强大的awk展示一个替代方案;我现在还没有更新我的答案,因为这会导致重复的内容——现在也提到你的答案。我在理解这里发生的所有符号时遇到了一些重大问题。你能反褶积每一个都意味着什么吗?这不起作用。我举的例子可能太简单了。模式可能并不总是一个数字。而file.txt文件的每一行可能都有复杂的字符串。就像1\u other text\n leadingtext+\u 2\u other text\n 3\n 4text一样,出于某种原因,我无法在注释中添加换行符,但想象一下这些换行符。这不应该在行的开头锚定以避免部分匹配吗?出于某种原因,其他答案不起作用,但这一个奇迹般地起作用。我会选择它作为正确答案,谢谢你波通!还有,你能给我解释一下这些符号是怎么回事吗?我希望能够添加此技能集,但我无法理解这些技能的组合
符号操作,因为我不习惯使用sed。这对我不起作用。我的实际案例比我贴的假例子要复杂得多。但我不确定复杂的地方在哪里,这样我才能制定出正确的编辑。如果我删除了所有空格字符,这行吗?在这种情况下,你会如何重新制作?我认为我在最初的示例中添加的空格可能会使这比它应该的简单得多,因此您发布的代码与Im的操作并不匹配doing@Tom如果你简化了你的例子,以至于这个例子的解决方案不能解决原来的问题,那么你简化的太多了;消除所有空格字符的确切含义是什么?哪些空格字符?请注意,Walter A的解决方案基本上是相同的,但更直接。
1 text_PUT FLAG HERE
2 text
3 text_PUT FLAG HERE
4 text
5 text
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
sed 's#.*#/&/s/$/_PUT FLAG HERE/#' pattern.txt | sed -f - file
# 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