0

I'm trying to run this command called codemaker which takes a filename as input but then writes the output to stdout instead of back to the file, so I have to redirect stdout to that file. That works fine, but I want to do this for a whole bunch of files at once, so I came up with this (based on https://stackoverflow.com/a/845928/65387):

ctouch() {
  xargs -t -i -0 sh -c 'codemaker "$1" > "$1"' -- {} <<<"${(ps:\0:)@}"
}

But I can't quite get the syntax right. It looks like it's treating everything as a single arg still:

❯ ctouch foo.h bar.cc                                                                                                      
sh -c 'codemaker "$1" > "$1"' -- 'foo.h bar.cc'$'\n'

Whereas I just want to run 2 commands:

codemaker foo.h > foo.h
codemaker bar.cc > bar.cc

How do I make an alias/function for that?

(And no, I'm not sure about that <<<"${(ps:\0:)@}" bit either. Really hard to Google. I want the usual "$@" to expand with null separators to feed to xargs)

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Does `codemaker foo.h > foo.h` actually work? Wouldn't that clobber the input file before the program even runs? – rowboat Sep 10 '22 at 07:58
  • 1
    @rowboat There is no input file actually, `foo.h` doesn't exist prior. `codemaker` needs a filepath because it will generate different things based on the path/filename and the extension. – mpen Sep 11 '22 at 18:44
  • 1
    Why don't you loop over the files? – user1934428 Sep 12 '22 at 06:49

2 Answers2

2

I don't see a compelling reason to use xargs in your case. You just create additional processes unnecessarily (one for xargs, plus for each argument, one shell process).

A simpler solution (and IMO easier to understand) would be to do it with this zsh-function:

ctouch() {
  for f
  do
    codemaker $f >$f
  done
}
user1934428
  • 19,864
  • 7
  • 42
  • 87
  • For some reason I didn't even think of that xargs can run stuff in parallel but performance isn't an issue here. – mpen Sep 12 '22 at 17:51
  • It can, but in the way you used `xargs`, you let it process the arguments sequentially. That's why I did not think that you wanted parallelism. BTW, you can also parallelize the loop, by running `codemaker` in the background. Another, more flexible, approach would be to use [gnu parallel](https://www.gnu.org/software/parallel/). We use `xargs` mainly for cases, where the command to be invoked allows for an _arbitrary number_ of arguments, and we want to feed to each invocation as many arguments as can fit into the commandline. – user1934428 Sep 13 '22 at 07:47
1

I think this is a lot easier to just do with printf.

ctouch() {
  printf -- '%s\0' "$@" | xargs -t -i -0 sh -c 'codemaker "$1" > "$1"' -- {}
}
mpen
  • 272,448
  • 266
  • 850
  • 1,236