How to check if sed has changed a file

后端 未结 9 1183
难免孤独
难免孤独 2020-12-01 06:57

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

Basically, I want to know if the file has been changed

相关标签:
9条回答
  • 2020-12-01 07:10

    You could use awk instead:

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

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

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

    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
    

    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.

    0 讨论(0)
  • 2020-12-01 07:13
    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>
    

    Example: The command will produce the following output, stating the number of changes made/file.

    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) 
    

    Note: I have removed -i from the above command , so it will not update the files for the people who are just trying out the command. If you want to enable in-place replacements in the file add -i after perl in above command.

    0 讨论(0)
  • 2020-12-01 07:17

    I believe you may find these GNU sed extensions useful

    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.
    

    and

    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.
    

    It seems like exactly what are you looking for.

    0 讨论(0)
  • 2020-12-01 07:19

    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.

    0 讨论(0)
  • 2020-12-01 07:22

    Don't use sed to tell if it has changed a file; instead, use grep to tell if it is going to change a file, then use sed to actually change the file. Notice the single line of sed usage at the very end of the Bash function below:

    # 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
    }
    

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

    Usage:

    gs_replace_str "regex_search_pattern" "replacement_string" "file_path"
    

    Example: replace do with bo so that "doing" becomes "boing" (I know, we should be fixing spelling errors not creating them :) ):

    $ 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 
    

    Screenshot of the output:

    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
    0 讨论(0)
  • 2020-12-01 07:31

    This might work for you (GNU sed):

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

    Explanation:

    • /'"$old_pattern"'/{s//'"$new_pattern"'/;h} if the pattern space (PS) contains the old pattern, replace it by the new pattern and 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).
    0 讨论(0)
提交回复
热议问题