0

I have a React app where some files are in .ts and some are in .tsx

Currently in order to update the contents of both .ts and .tsx files, I have to run 2 separate commands:

internal-web main % cd src
src main % find . -name '*.tsx' -print0 | xargs -0 sed -i "" "s/SomeThing/SOME_THING/g"
src main % find . -name '*.ts' -print0 | xargs -0 sed -i "" "s/SomeThing/SOME_THING/g"

Is there a way I can combine this to one command so that I can update both .ts and .tsx files at once?

bigpotato
  • 275
  • 1
  • 3
  • 10

3 Answers3

5

You can do:

find . \( -name '*.tsx' -o -name '*.ts' \) -print0 | 
    xargs -0 sed -i "" "s/SomeThing/SOME_THING/g"

Or, simpler and more portable (and avoids a sed error if not file is found):

find . \( -name '*.tsx' -o -name '*.ts' \) -exec sed -i "" "s/SomeThing/SOME_THING/g" {} +

In any case, note that -i is not a standard option to sed. sed -i "" usage suggests the FreeBSD implementation of sed (also found on macOS). Most other sed implementations either don't support a -i or need the "" to be omitted.

terdon
  • 242,166
  • @stéphane does FreeBSD or more specifically macOS find support -exec ... +? I know it's been added to POSIX, but the macOS man page I checked doesn't mention it, so if the OP is using macOS it seems like the small increase in efficiency that + provides might not be worth it. Or is the increase more than I think, and/or the + certainly supported by macOS? – terdon Jul 30 '22 at 12:19
  • it's been in POSIX for decades, feature originally from SysV in the mid 80s. macOS has been certified as compliant for decades as well. GNU and some BSDs have added them relatively late, but that still was in the 2000s IIRC – Stéphane Chazelas Jul 30 '22 at 12:45
  • Huh, OK. I guess that's a very old man page then, it just seems odd as macOS itself isn't that old. – terdon Jul 30 '22 at 12:57
  • See https://www.unix.com/man-page/mojave/1/find/ for macos mojave man page. – Stéphane Chazelas Jul 30 '22 at 13:09
  • Ah perfect, thanks @StéphaneChazelas! – terdon Jul 30 '22 at 13:09
  • Note that switching from ; to + is not a small increase unless the files are very big or there's only a handful on them. With -exec ... ';', for each file, you're forking a new process, executing sed in them which also means loading it, the dynamic linker, all the libraries, do the dynamic linking, do the initialisations (locale, etc)... Using -exec ... {} + like with xargs also allows the reporting of the failure of any sed invocation via the exit status of find (or xargs). – Stéphane Chazelas Jul 30 '22 at 13:20
1

You can use a combined filter with -regex instead of -name. Given the following files

src/
src/subdir-2
src/subdir-2/not-interesting.tsn
src/subdir-2/bar.ts
src/subdir-2/bar.tsx
src/subdir-2/not-interesting.not
src/subdir-1
src/subdir-1/not-interesting.tsn
src/subdir-1/foo.tsx
src/subdir-1/foo.ts
src/subdir-1/not-interesting.not

you can filter the .ts and .tsx files with find src/ -regex '.+\.tsx?$'

src/subdir-2/bar.ts
src/subdir-2/bar.tsx
src/subdir-1/foo.tsx
src/subdir-1/foo.ts
Sheldon
  • 200
  • 3
    Note that -regex is a GNU extension, or at least, not part of POSIX find so it might not be available to the OP. – terdon Jul 29 '22 at 17:58
1

There is no need to run find in this case. If using the bash, shell (at least version 4.0, preferably 5.0 or above which has fixed a few bugs in the globstar implementation), you can use globbing instead:

sed -i "" "s/SomeThing/SOME_THING/g" ./**/*.ts?(x)

This uses the extended matching operators, specifically:

'?(PATTERN-LIST)'
     Matches zero or one occurrence of the given patterns.

You'll need to enable globstar first (shopt -s globstar) and extglob (shopt -s extglob).

Beware it skips hidden files, add shopt -s dotglob if you want them to be processed as well as find would.

r_31415
  • 516
  • This would also match e.g a.txAs. Did you mean something like **/*.ts{x,} instead? Also may fail if there are too many results/ too many too long results. – FelixJN Jul 29 '22 at 20:40
  • @FelixJN No, it shouldn't match files with .txAs. I will clarify what it does in the answer. – r_31415 Jul 29 '22 at 20:44
  • I guess it is possible to have it fail if there are too many arguments, but at least in my system, it is set to 2,097,152 – r_31415 Jul 29 '22 at 20:50
  • When I run this it shows me an error sed: 1: "**/*.ts*(x)": invalid command code * – bigpotato Jul 29 '22 at 21:22
  • @bigpotato Did you enable globstar and extglob? Are you using bash? I just tested this command and is working correctly. – r_31415 Jul 29 '22 at 21:45
  • getconf ARG_MAX doesn't return the max number of arguments, it's more a limit on the cumulative size of the arguments (+ environment variables) which includes the text of those arguments but also the size of the pointers to those arguments (how it's calculated varies significantly between systems and versions thereof). In practice, with a 2MiB ARG_MAX you will likely not be able to pass many more than 100000 file paths. – Stéphane Chazelas Jul 30 '22 at 12:50
  • Yes, I should have specified I was referring to ARG_MAX. I'm pretty sure it doesn't matter either way, since it is clear OP doesn't have that many typescript files. – r_31415 Jul 30 '22 at 15:06