I have a directory that contains some subdirectories. I know there are two types of files, such as *A*.txt and *B*.txt, contain an "oldString" under that directory. I want to replace them with a "newString". Can I do this in one command? That is can I add the "*B*.txt" somewhere in this command:

find . -type f -name "*A*.txt" -exec sed -i 's/oldString/newString/g' {} \;
  • So... what you're really asking is how to match multiple -name patterns in a find command? – steeldriver Dec 10 '19 at 14:18
  • yes, you are right. – peterboston Dec 10 '19 at 14:20

You can try:

find . -type f \( -name "*A*.txt" -o -name "*B*.txt" \) \
  -exec sed -i 's/oldString/newString/g' {} +

(here also using + instead of ; to avoid running one sed invocation per file; also has the benefit of returning a non-zero exit status if any of the sed invocations return with a non-zero exit status).

  • if I add "-path", can I use the -o there: find . -type f ( -name "A.txt" -o -name "B.txt" ) ( -path "oneFolder" -o -path "anotherFolder" ) -exec sed -i 's/oldString/newString/g' {} + – peterboston Dec 10 '19 at 14:58

If it really is just one letter, you can do this:

find . -type f -name "*[AB]*.txt" -exec sed -i 's/oldString/newString/g' {} \;

To be totally clear, this will only work if using a single letter as a match.

  • It should be noted though that that approach only works with single-character patterns (there's no equivalent to match on *foo*.txt or *bar*.txt for instance). – Stéphane Chazelas Dec 10 '19 at 14:32

If you have GNU find, you could use -regex instead of -name, and do something like

find . -type -f -regextype egrep -regex "(.*A.*|.*B.*)\.txt" -exec sed -i 's/oldString/newString/g' {} \;

Note that if the pattern can also appear in the directory path to the files, the regular expression needs to be modified (as per @StéphaneChazelas' comment) to be

-regex ".*(A|B)[^/]*\.txt"

to exclude anything where a / occurs after the patterns.

You can check if the regular expression type egrep is available using

find -regextype help
  • -regex matches on the full path, so it would match on a ./AFTER/foo.txt for instance. You'd need -regex '.*/[^/]*(A|B)[^/]*\.txt' for the A/B to only be looked for in the file's name. – Stéphane Chazelas Dec 10 '19 at 14:28
  • 1
    -regex '.*(A|B)[^/]*\.txt' should be enough. – Stéphane Chazelas Dec 10 '19 at 14:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.