How to get filenames when using find and sed

12,183

Solution 1

Your sed command (with proper quoting):

sed 's/str1/str2/g'

This will change all occurrences of str1 into str2. A list of files containing str1 can be had from grep -l 'str1':

find . -type f \( -name '*.txt' -o -name '*.git' \) \
    -exec grep -l 'str1' {} \; \
    -exec sed -i 's/str1/str2/g' {} + >changelist.txt

Here, grep -l will provide a list of pathnames that will be redirected into changelist.txt. It will also act like a filter for sed so that sed is only run on files that contain the pattern. sed -i will then make the changes in the files (and remain quiet).

Alternatively, let find print the pathnames of the files that contain the string:

find . -type f \( -name '*.txt' -o -name '*.git' \) \
    -exec grep -q 'str1' {} \; \
    -print \
    -exec sed -i 's/str1/str2/g' {} + >changelist.txt

Related:

Solution 2

sed -i rewrites the file (actually makes full new copies of the files) regardless of whether any of the s commands in the sed script succeeded or not.

Here, you'd want to avoid running sed -i on files that don't contain str1. With GNU tools:

find . -type f \( -name "*.txt" -o -name "*.git" \) -size +3c \
  -exec grep -lZ str1 {} + |
  while IFS= read -rd '' file; do
    sed -i 's/str1/str2/g' "$file" &&
      printf '%s\n' "$file"
  done

That runs one sed for each of the files that contain str1 and prints the file names if sed has been successful (for which there has been no error in creating the new version of the file).

Or you can run one grep and sed per file:

find . -type f \( -name "*.txt" -o -name "*.git" \) \( -size +3c \
  -exec grep -q str1 {} \; \
  -exec sed -i 's/str1/str2/g' {} \; \
  -printf '"%p" was modified\n' \
    -o -printf '"%p" was not modified\n"' \)
Share:
12,183

Related videos on Youtube

KLMM
Author by

KLMM

Updated on September 18, 2022

Comments

  • KLMM
    KLMM over 1 year

    I am writing a script to apply sed on certain files and then list files that have been changed so that I know which have been modified.

    This is how I am finding and then using sed:

    find . -type f -a \( -name "*.txt" -o -name "*.git"\) -a -exec sed -i -e "s/"str1"/"str2"/g" {} +
    

    How do I print the file name of the changed files? I would like to print it in a sorted order so it's easier to read.

    When using only sed we can do this:

    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
    

    But how do I do this when using find and sed together? I checked the man page and tried a bunch of stuff but nothing worked.

    • Admin
      Admin over 8 years
      I'm not exactly sure what you're trying to achieve, can you show a list of files, and what output you're expecting to see.
    • Admin
      Admin over 8 years
      @EightBitTony I have added examples, please take a look
    • Admin
      Admin over 8 years
      You just add -print after your -exec, it will only be executed if the -exec was successful e.g. find . -type f \( -name \*.git -o -name \*.txt \) -exec sed -i 'blah_blah' {} \; -print. Sure, you'll have to sort the output then.
    • Admin
      Admin over 8 years
      @don_crissti using print giving an error "-print: command not found".
    • Admin
      Admin over 8 years
      @don_crissti yes this worked for printing the file but how can I get them in a sorted order because we are not storing then output in any variable and there is no flag that we can use to sort. How Can i achieve sorted order?
    • Admin
      Admin over 8 years
      I found that your suggestion of using print, prints all the files having the extension .txt or .git. I only want to print files that have been modified not all that match the pattern
    • Admin
      Admin over 8 years
      If you use the AST find you can sort its output.
  • terdon
    terdon over 8 years
    Please edit your answer so that it actually provides an answer. At the moment, this is a comment simply giving a suggestion.
  • KLMM
    KLMM over 8 years
    @unxnut I am unable to understand your answer, I do have a high level idea of what needs to be done. Please provide some code solution, thanks
  • KLMM
    KLMM over 8 years
    @unxnut Is it possible to achieve this in a single script(will using routines work)? And how do we get sorted order?
  • unxnut
    unxnut over 8 years
    Since the filenames for the session are saved in changelog, all you have to do is sort changelog to get the file in sorted order at the end of the find command.