bash 如何检查sed是否更改了文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12144158/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 03:08:05  来源:igfitidea点击:

How to check if sed has changed a file

linuxbashshellubuntused

提问by breakdown1986

I am trying to find a clever way to figure out if the file passed to sed has been altered successfully or not.

我试图找到一种聪明的方法来确定传递给 sed 的文件是否已成功更改。

Basically, I want to know if the file has been changed or not without having to look at the file modification date.

基本上,我想知道文件是否已更改,而不必查看文件修改日期。

The reason why I need this is because I need to do some extra stuff if sed has successfully replaced a pattern.

我需要这个的原因是因为如果 sed 成功替换了一个模式,我需要做一些额外的事情。

I currently have:

我目前有:

    grep -q $pattern $filename
    if [ $? -eq 0 ]
    then
        sed -i s:$pattern:$new_pattern: $filename
                # DO SOME OTHER STUFF HERE
    else
        # DO SOME OTHER STUFF HERE
    fi

The above code is a bit expensive and I would love to be able to use some hacks here.

上面的代码有点贵,我希望能够在这里使用一些技巧。

采纳答案by William Pursell

You could use awkinstead:

你可以awk改用:

awk '$0 ~ p { gsub(p, r); t=1} 1 END{ exit (!t) }' p="$pattern" r="$repl"

awk '$0 ~ p { gsub(p, r); t=1} 1 END{ exit (!t) }' p="$pattern" r="$repl

I'm ignoring the -ifeature: you can use the shell do do redirections as necessary.

我忽略了该-i功能:您可以根据需要使用 shell 进行重定向。

Sigh. Many comments below asking for basic tutorial on the shell. You can use the above command as follows:

叹。下面的许多评论要求提供有关 shell 的基本教程。您可以按如下方式使用上述命令:

if awk '
sed -i "s/$pattern/$new_pattern/w changelog.txt" "$filename"
if [ -s changelog.txt ]; then
    # CHANGES MADE, DO SOME STUFF HERE
else
    # NO CHANGES MADE, DO SOME OTHER STUFF HERE
fi
~ p { gsub(p, r); t=1} 1 END{ exit (!t) }' \ p="$pattern" r="$repl" "$filename" > "${filename}.new"; then cat "${filename}.new" > "${filename}" # DO SOME OTHER STUFF HERE else # DO SOME OTHER STUFF HERE fi

It is not clear to me if "DO SOME OTHER STUFF HERE" is the same in each case. Any similar code in the two blocks should be refactored accordingly.

我不清楚“在这里做一些其他的事情”是否在每种情况下都相同。应该相应地重构两个块中的任何类似代码。

回答by aureliandevel

A bit late to the party but for the benefit of others, I found the 'w' flag to be exactly what I was looking for.

聚会有点晚了,但为了其他人的利益,我发现 'w' 标志正是我想要的。

t label

If a s/// has done a successful substitution since the last input line
was read and since the last t or T command, then branch to label; if
label is omitted, branch to end of script.

changelog.txtwill contain each change (ie the changed text) on it's own line. If there were no changes, changelog.txtwill be zero bytes.

changelog.txt将在其自己的行上包含每个更改(即更改的文本)。如果没有变化,changelog.txt将是零字节。

A really helpful sed resource (and where I found this info) is http://www.grymtheitroade.com/Unix/Sed.html.

一个非常有用的 sed 资源(以及我在何处找到了此信息)是http://www.grymtheitroade.com/Unix/Sed.html

回答by hostmaster

I believe you may find these GNU sed extensions useful

我相信你会发现这些 GNU sed 扩展很有用

q [exit-code]

Immediately quit the sed script without processing any more input, except 
that if auto-print is not disabled the current pattern space will be printed. 
The exit code argument is a GNU extension.

and

sed -i.bak '/'"$old_pattern"'/{s//'"$new_pattern"'/;h};${x;/./{x;q1};x}' file || echo changed

It seems like exactly what are you looking for.

这似乎正是你在寻找什么。

回答by potong

This might work for you (GNU sed):

这可能对你有用(GNU sed):

sed -i.bak s:$pattern:$new_pattern: "$filename"
if ! diff "$filename" "$filename.bak" &> /dev/null; then
  echo "changed"
else
  echo "not changed"
fi
rm "$filename.bak"

Explanation:

解释:

  • /'"$old_pattern"'/{s//'"$new_pattern"'/;h}if the pattern space (PS) contains the old pattern, replace it by the new patternand copy the PS to the hold space (HS).
  • ${x;/./{x;q1};x}on encountering the last line, swap to the HS and test it for the presence of any string. If a string is found in the HS (i.e. a substitution has taken place) swap back to the original PS and exit using the exit code of 1, otherwise swap back to the original PS and exit with the exit code of 0(the default).
  • /'"$old_pattern"'/{s//'"$new_pattern"'/;h}如果模式空间 (PS) 包含old pattern,则将其替换为new pattern并将 PS 复制到保持空间 (HS)。
  • ${x;/./{x;q1};x}在遇到最后一行时,切换到 HS 并测试它是否存在任何字符串。如果在 HS 中找到字符串(即发生了替换),则交换回原始 PS 并使用退出代码 退出1,否则交换回原始 PS 并以退出代码0(默认)退出。

回答by perreal

You can diff the original file with the sed output to see if it changed:

您可以将原始文件与 sed 输出进行比较,以查看它是否已更改:

# Usage: `gs_replace_str "regex_search_pattern" "replacement_string" "file_path"`
gs_replace_str() {
    REGEX_SEARCH=""
    REPLACEMENT_STR=""
    FILENAME=""

    num_lines_matched=$(grep -c -E "$REGEX_SEARCH" "$FILENAME")
    # Count number of matches, NOT lines (`grep -c` counts lines), 
    # in case there are multiple matches per line; see: 
    # https://superuser.com/questions/339522/counting-total-number-of-matches-with-grep-instead-of-just-how-many-lines-match/339523#339523
    num_matches=$(grep -o -E "$REGEX_SEARCH" "$FILENAME" | wc -l)

    # If num_matches > 0
    if [ "$num_matches" -gt 0 ]; then
        echo -e "\n${num_matches} matches found on ${num_lines_matched} lines in file"\
                "\"${FILENAME}\":"
        # Now show these exact matches with their corresponding line 'n'umbers in the file
        grep -n --color=always -E "$REGEX_SEARCH" "$FILENAME"
        # Now actually DO the string replacing on the files 'i'n place using the `sed` 
        # 's'tream 'ed'itor!
        sed -i "s|${REGEX_SEARCH}|${REPLACEMENT_STR}|g" "$FILENAME"
    fi
}

回答by perreal

I know it is a old question and using awk instead of sed is perhaps the best idea, but if one wants to stick with sed, an idea is to use the -w flag. The file argument to the w flag only contains the lines with a match. So, we only need to check that it is not empty.

我知道这是一个老问题,使用 awk 而不是 sed 可能是最好的主意,但如果有人想坚持使用 sed,一个想法是使用 -w 标志。w 标志的文件参数只包含匹配的行。所以,我们只需要检查它是否为空。

回答by Gabriel Staples

Don't use sedto tell if it haschanged a file; instead, use grepto tell if it is going tochange a file, then use sedto actually change the file. Notice the single line of sedusage at the very endof the Bash function below:

不要sed用来判断它是否改变了一个文件;相反,用于grep判断它是否要更改文件,然后用于sed实际更改文件。请注意下面 Bash 函数末尾的单行sed用法:

gs_replace_str "regex_search_pattern" "replacement_string" "file_path"

Place that in your ~/.bashrc file, for instance. Close and reopen your terminal and then use it.

例如,将它放在您的 ~/.bashrc 文件中。关闭并重新打开您的终端,然后使用它。

Usage:

用法:

$ gs_replace_str "do" "bo" test_folder/test2.txt 

9 matches found on 6 lines in file "test_folder/test2.txt":
1:hey how are you doing today
2:hey how are you doing today
3:hey how are you doing today
4:hey how are you doing today  hey how are you doing today  hey how are you doing today  hey how are you doing today
5:hey how are you doing today
6:hey how are you doing today?
$SHLVL:3 

Example: replace dowith boso that "doing" becomes "boing" (I know, we should be fixing spelling errors not creating them :) ):

示例:替换dobo使“doing”变成“boing”(我知道,我们应该修复拼写错误而不是创建它们:)):

changes=""
changes+=$(sed -i '' "s/$to_replace/$replacement/g w /dev/stdout" "$f")
if [ "$changes" != "" ]; then
  echo "CHANGED!"
fi

Screenshot of the output:

输出截图:

enter image description here

在此处输入图片说明

References:

参考:

  1. https://superuser.com/questions/339522/counting-total-number-of-matches-with-grep-instead-of-just-how-many-lines-match/339523#339523
  2. https://unix.stackexchange.com/questions/112023/how-can-i-replace-a-string-in-a-files/580328#580328
  1. https://superuser.com/questions/339522/counting-total-number-of-matches-with-grep-instead-of-just-how-many-lines-match/339523#339523
  2. https://unix.stackexchange.com/questions/112023/how-can-i-replace-a-string-in-a-files/580328#580328

回答by acecilia

In macos I just do it as follows:

在 macos 中,我只是这样做:

##代码##

I checked, and this is faster than md5, cksumand shacomparisons

我查了一下,这是不是更快md5cksum并且sha比较