Search code examples
linuxfindrsyncxargscp

Linux/Cygwin recursively copy file change extension


I'm looking for a way to recursively find files with extension X (.js) and make a copy of the file in the same directory with extension Y (.ts).

e.g. /foo/bar/foobar.js --> /foo/bar/foobar.js and /foo/bar/foobar.ts

/foo/bar.js --> /foo/bar.js and /foo/bar.ts etc etc

My due diligence: I was thinking of using find & xargs & cp and brace expansion (cp foobar.{js,ts}) but xargs uses the braces to denote the list of files passed from xargs. This makes me sad as I just recently discovered the awesome-sauce that is brace expansion/substitution.

I feel like there has to be a one-line solution but I'm struggling to come up with one.

I've found ideas for performing the task: copying the desired to a new directory and then merging this directory with the new one; recursively run a renaming script in each directory; copy using rsync; use find, xargs and cpio.

As it stands it appears that running a renaming script script like this is what I'll end up doing.


Solution

  • find . -name "*.js" -exec bash -c 'name="{}"; cp "$name" "${name%.js}.ts"' \;
    

    Using find, you can execute a command directly on a file that you've found, by using the -exec option; you don't need to pipe it through xargs. It takes the command name followed by arguments to the command, followed by a single argument ;, which you have to escape to avoid the shell interpreting it. find will replace any occurrence of {} in the command name or arguments with the file found.

    In order call a command with the appropriate ending substituted, there are multiple approaches you can take, but a simple one is to use Bash's parameter expansion. You need to define a shell parameter that contains the name (in this case, I creatively chose name={}), and then you can use parameter expansion on it. ${variable%suffix} strips off suffix from the value of $variable; I then add on .ts to the end, and have the name I'm looking for.