26

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

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

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

我目前有:

    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

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

4

10 回答 10

37

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

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

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

http://www.grymoire.com/Unix/Sed.html是一个非常有用的 sed 资源(我在哪里找到了这个信息)。

于 2015-03-10T14:42:47.173 回答
10

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

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.

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.

看起来正是你在寻找什么。

于 2012-08-27T15:25:57.040 回答
10

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

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

解释:

  • /'"$old_pattern"'/{s//'"$new_pattern"'/;h}如果模式空间 (PS) 包含old pattern,则将其替换为new pattern并将 PS 复制到保持空间 (HS)。
  • ${x;/./{x;q1};x}在遇到最后一行时,切换到 HS 并测试它是否存在任何字符串。如果在 HS 中找到一个字符串(即发生了替换),则切换回原始 PS 并使用退出代码 退出1,否则切换回原始 PS 并使用退出代码退出0(默认)。
于 2012-08-27T16:24:41.023 回答
5

您可以awk改用:

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

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

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

if awk '$0 ~ 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

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

于 2012-08-27T14:49:36.850 回答
5

您可以使用 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"
于 2012-08-27T14:49:50.980 回答
1

在macos中,我只是这样做:

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

我检查了,这比md5,cksumsha比较快

于 2020-05-14T22:15:50.880 回答
0

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

于 2016-03-06T08:00:59.087 回答
0
perl -sple '$replaced++ if s/$from/$to/g;
                END{if($replaced != 0){ print "[Info]: $replaced replacement done in $ARGV(from/to)($from/$to)"}
                else {print "[Warning]: 0 replacement done in $ARGV(from/to)($from/$to)"}}' -- -from="FROM_STRING" -to="$DESIRED_STRING" </file/name>

示例:该命令将产生以下输出,说明所做的更改数/文件。

perl -sple '$replaced++ if s/$from/$to/g;
END{if($replaced != 0){ print "[Info]: $replaced replacement done in $ARGV(from/to)($from/$to)"}
else {print "[Warning]: 0 replacement done in $ARGV(from/to)($from/$to)"}}' -- -from="timeout" -to="TIMEOUT" *
[Info]: 5 replacement done in main.yml(from/to)(timeout/TIMEOUT)
[Info]: 1 replacement done in task/main.yml(from/to)(timeout/TIMEOUT)
[Info]: 4 replacement done in defaults/main.yml(from/to)(timeout/TIMEOUT)
[Warning]: 0 replacement done in vars/main.yml(from/to)(timeout/TIMEOUT) 

注意:我已从-i上述命令中删除,因此它不会为刚刚尝试该命令的人更新文件。如果要在文件中启用就地替换,请在上述命令-i之后添加。perl

于 2020-09-23T15:32:23.233 回答
0

检查 sed 是否更改了许多文件

  • 递归替换一个目录中的所有文件
  • 生成所有修改文件的列表

两个阶段的解决方法:匹配+替换

g='hello.*world'
s='s/hello.*world/bye world/g;'
d='./' # directory of input files
o='modified-files.txt'

grep -r -l -Z -E "$g" "$d" | tee "$o" | xargs -0 sed -i "$s"

中的文件路径以$o零分隔

于 2022-01-27T14:55:38.510 回答
-1

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

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

    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
}

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

用法:

gs_replace_str "regex_search_pattern" "replacement_string" "file_path"

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

$ 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 

输出截图:

在此处输入图像描述

参考:

  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
于 2020-04-15T20:52:24.213 回答