xargs failed to run one script in sub-folder - xargs

cd folder1
ls | xargs -t -I {} {}/MyScript.sh param1 param2 &
The above command doesn't work.
I am not sure why {} is not replaced by sub-folder.
How can I fix the issue?
summary for the question:
below works:
ls | xargs -t -I '{}' ksh '{}'/MyScript.ksh param1 param2

After some experimentation, this doesn't work:
ls | xargs -t -I {} {}/MyScript.sh param1 param2
But this does:
ls | xargs -t -I {} sh {}/MyScript.sh param1 param2
A close reading of the man page reveals why:
xargs [...] [command [initial-arguments]]
-I replace-str
Replace occurrences of replace-str in the initial-arguments
with names read from standard input.
If the {} is first, it is part of the command, not the initial-arguments, and thus is not replaced. So, you need to arrange things so that the {} is always part of the arguments, and not the command. In your case, this can easily be done by using sh {}/MyScript.sh rather than invoking MyScript directly.
(Note: I'm using bash here, where {} with nothing inside it doesn't need to be quoted. Other shells may need to quote it.)

Related

Unable to pass quoted string to rm directly with xagrs

To find some file with grep and delete them with rm I tried following command -
$ ls | grep 2019 | xargs -i rm \"{}\"
That did not work. Got the following error message -
rm: cannot remove '"2019-05-10 00:00:00-TO-2019-05-10 23:59:59_PDT_disconnection_info.csv"': No such file or directory
Looks like xargs is taking quotes literally. So, tried echoing instead of passing directly -
ls | grep 2019 | xargs -i echo \"{}\" | xargs rm
This worked.
Why does not it work without echoing?
The proper quoting is done by xargs, there is no need to quote it again. Just:
... | xargs -i rm {}
Or better, because rm accepts multiple arguments, just do:
... | xargs rm
Why does not it work without echoing?
When not used with -i, -I, -d or similar, the xargs utility handles proper quoting in input with double or single quotes or escaping with a backslash. The quotes are removed by the second xargs and rm is passed unquoted string. From man xargs:
.... xargs reads
items from the standard input, delimited by blanks (which can be
protected with double or single quotes or a backslash)
Compare:
$ echo "\e\r\t\q\e" | xargs -t echo
echo ertqe
ertqe
Also see Why you shouldn't parse the output of ls(1).

How to understand xargs

Saying that I have two files t1 and t2, they have the same content: abc.
Now I want to delete all files, who contains the string abc.
So I tried to execute the command: grep -rl abc . | rm but it doesn't work.
Then I add xargs: grep -rl abc . | xargs rm and it works.
I can't understand clearly what xargs did.
grep puts the output as stdout. But rm cannot process data from stdin (the pipe links both).
You want instead, that the output of grep is put as argument of rm. So xargs command "convert" stdin into arguments of xargs first argument, and it call the command (the first argument).
As alternative, you could do
rm `grep -rl abc .`
or
rm $(grep -rl abc .)
But xargs handles well also the case where there are too many arguments for a single call of the command. The above command will give you shell error (argument string too long).
rm doesn't read from standard input (except when prompting, like with -i) but takes its arguments on the command line. That's what xargs does for you: read things from standard input and give them to rm as arguments.
Example with echo:
$ (echo a; echo b; date) | xargs echo
a b tor 12 apr 2018 14:18:50 CEST

xargs - place the argument in a different location in the command

Let's say I want to write a script that does ls with a prefix for each filename. I tried this
ls | xargs -n 1 echo prefix_
But the result was
prefix_ first_file
prefix_ second_file
...
How can I remove the space between the prefix and the filename? I.e. how to I make xargs put the variable after the command, without space? (Or in any other place for that matter)
The solution: -I
-I lets you name your argument and put it anywhere you like. E.g.
ls | xargs -n 1 -I {} echo prefix_{}
(replace {} with any string)

Shell command xargs & sed doesn't work

I just want to batch modify the suffix of the files,but it doesn't work!
The command line I used as below:
ls *html | xargs -I{} echo "\`echo {} | sed 's/html/css/g'\`"
However, when I just used ls *html,it shows:
file1.html file2.html file3.html file4.html file5.html
used ls *html | sed 's/html/css/g',it shows as I expected!
like this:
file1.css file2.css file3.css file4.css file5.css
I work on Mac OS. Could anyone give me some suggestions?
Thans in advance.
Because the backquotes are in double quotes, it gets executed immediately by the shell and not by xargs on each file.
The result is the same as
ls *html | xargs -I{} echo "{}"
However, if you use single quotes, you run into other troubles. You end up having to do something like this:
ls *html | xargs -I{} sh -c 'echo `echo {} | sed '\''s/html/css/g'\''`'
but it gets to be a mess, and we haven't even got to the actual renaming yet.
Using a loop is a bit nicer:
for file in *html; do
newname=${file%html}css
mv "$file" "$newname"
done
Using GNU Parallel:
ls *html | parallel echo before {} after {.}.css

Multiple commands with find and xargs, also accounting for special characters

In OS X I am trying to combine the following two commands into a single command in a bash script, so that find operates once only. The files used by find contain spaces and special characters.
Command 1:
find /path -print0 | tr '\n' '\0' | xargs -0 chmod -N
Command 2:
find /path -print0 | tr '\n' '\0' | xargs -0 xattr -c
Both the above commands work.
I understand from 'Make xargs execute the command once for each line of input' that multiple commands can be executed through xargs with something like
find /path -print0 | xargs -0 -I '{}' sh -c 'command1 {} ; command2 {}'
However, my attempt to combine the commands with
find /path -print0 | tr '\n' '\0' | xargs -0 -I '{}' sh -c 'chmod -N {} ; xattr -c {}'
results in multiple errors for each file and folder in the /path, such as
chmod: Failed to clear ACL on file {}: No such file or directory
xattr: No such file: {}
sh: -c: line 0: syntax error near unexpected token `('
Is anyone able to help? Thank you in advance.
Try the following:
find /path -exec sh -c 'chmod -N "$#"; xattr -c "$#"' - {} +
-exec ... +, passes (typically) all matching paths to the specified command, which is them most efficient approach.
Both chmod and xattr support multiple file operands, so this approach is feasible.
find properly retains argument boundaries when passing substituting the paths for {}, so it would even handle filenames with embedded newlines correctly.
Incidentally: I'm unclear on what the purpose of tr '\n', '\0' in your code is, given that you already output \0-separated paths thanks to -print0.
Note the - as the first (dummy) argument passed to sh -c, because the first argument will become $0.
As for the problem with your original command:
I can't explain the specific symptoms, but one problem is that you're not quoting the {} instances inside your shell command, which makes them subject to word splitting (breaks file paths with embedded spaces into multiple arguments).

Resources