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

时间:2012-08-27 14:38:28

标签: linux bash shell ubuntu sed

我试图找到一种聪明的方法来确定传递给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

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

9 个答案:

答案 0 :(得分:21)

派对有点晚,但为了别人的利益,我找到了' 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将为零字节。

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

答案 1 :(得分:9)

我相信您可能会发现这些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.

看起来你正在寻找什么。

答案 2 :(得分:6)

您可以改为使用awk

awk "/$pattern/"' { gsub( "'"$pattern"'", "'"$repl"'" ); t=1 }
    1; END{ exit( !t )}'

我忽略了-i功能:你可以根据需要使用shell做重定向。

答案 3 :(得分:6)

这可能适合你(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并退出,退出代码为{ {1}}(默认值)。

答案 4 :(得分:4)

您可以使用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"

答案 5 :(得分:0)

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

答案 6 :(得分:0)

不要使用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"

示例:将do替换为bo,以使“做”变成“无聊”(我知道,我们应该修复不创建它们的拼写错误:)):

$ 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 

输出的屏幕截图:

enter image description here

参考文献:

  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

答案 7 :(得分:0)

在macOS中,我只按以下步骤操作:

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

我检查了,这比md5cksumsha的比较要快

答案 8 :(得分: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