6

I need to use xargs to call a function in parallel in fish shell. I tried this:

#!/bin/fish

function func
    echo $argv
end

echo 'example1' 'example2' | xargs -n1 func

But I got this:

xargs: func: No such file or directory

So, how can I make it work?

Using bash this worked:

#!/bin/bash

function func {
    echo $1
}
export -f func

echo 'example1' 'example2' | xargs -n1  bash -c 'func $@' _

2 Answers2

7

Like Kurtis said, xargs won't work with functions. It can be made to work by launching another shell, but that's a hack.

What would probably work better for you is to not use xargs at all.

Instead of

echo 'example' | xargs func

just run func example.

In general, fish's command substitutions should be used for this.

So instead of

somecommand | xargs func

use

func (somecommand)

this will split somecommands output on newlines, which is strictly speaking more strict than xargs (which will split on "blanks" and newline by default, but it will allow shell-like quoting and escaping), which is typically what you want.

faho
  • 14,470
  • 2
  • 37
  • 47
  • I want to use xargs because of the -n1 option so I can call a function many times in parallel. There are another useful options on xargs too. Is there no way to call a function like a command, creating a new process? – Sérgio Mucciaccia Jun 12 '18 at 00:56
  • No. You'll have to call `fish -c` instead of the function directly. Which is also what your doing with bash, except that fish has no way of "exporting" a function, so it needs to be available globally (e.g. via autoloading) or `source`d explicitly. – faho Jun 13 '18 at 14:38
  • It seems I can't pass parameters throught `fish -c`. For example `sh -c 'cat $0' 'a.txt'` works and the argument a.txt is passed to cat, but `fish -c 'cat $argv' 'a.txt'` doesn't work. How can I pass a dynamic argument to the `fish -c` command? – Sérgio Mucciaccia Jun 13 '18 at 17:32
5

The xargs command requires and external command. You can't give it a function. What you're trying to do won't work with bash, zsh, ksh, or any similar shell. The way to do this is put the function in a file named func that is in your PATH:

#!/bin/fish

function func
    echo $argv
end

func $argv

Now that it is implemented as an external command (a shell script) you can use it with xargs: echo 'example' | xargs func.

Kurtis Rader
  • 6,734
  • 13
  • 20
  • It worked on bash making some modifications. Like in this answer: https://stackoverflow.com/questions/11003418/calling-shell-functions-with-xargs Is there no way to do something similar in fish? – Sérgio Mucciaccia Jun 12 '18 at 00:20
  • The bash `export -f some_func` feature is a bogosity in my opinion. The reason to use a shell function is because it can modify the state of the current shell. Exporting the function in that manner makes that impossible. And is thus misleading and silly since you can achieve the same effect using my solution. – Kurtis Rader Jun 12 '18 at 04:12
  • I know I can put then in separate scripts, but I have many functions here and I don't want to put each of them in a separate script for a matter of organization. – Sérgio Mucciaccia Jun 12 '18 at 14:25
  • Read my answer again, @SérgioMucciaccia. It works in bash only because bash has an anti-feature (i.e., something that should never have been implemented). A major point of shell functions is that they can modify the state of the shell. The `export -f some_func` feature, by definition, means that `some_func` can't modify the state of the current shell. In which case you should just implement it as a script. Because that is what is happening in the background. – Kurtis Rader Jun 13 '18 at 05:43