4

I have a question. Unfortunately, I didn't find any answer. How can I pass arguments to script which are result of another command. For example:

 ls | ./myscript.sh

I want to pass the result of ls to myscript. And if I execute the above command and the script is :

#!/bin/bash
read some
for arg in $@
do 
     grep $some $arg
done

then it didn't wait to read the some variable and the variable some gets its value from the result passed from ls command? How this actually works ? I want the following: To put to the script names of files (with command ls), after that the user to enter some string and I want to print every line which contain the entered word. I didn't know why the code above didn't work. Thanks in advance.

7
  • 1
    Please format your question with correctly marked code blocks (use backticks and the {} button on the toolbar. Also please give the exact contents of myscript.sh that are involved with reading input and show, if possible, the output/results you get and what you wanted to get. Commented May 11, 2015 at 18:44
  • Are you trying to populate some from the keyboard, rather than from ls? read just reads from standard input, which in the pipeline shown is the output of ls, not the terminal. Commented May 11, 2015 at 18:51
  • Ok, thanks, but what if I want to pass argument to the script with ls ? Commented May 11, 2015 at 18:56
  • 1
    FYI -- using output of ls programatically is a very bad idea. See mywiki.wooledge.org/ParsingLs Commented May 11, 2015 at 20:18
  • 1
    Also, for arg in $@ is not the same as for arg in "$@" (or simply for arg, as "$@" is the default target for a for loop), which would be what you wanted if you were passing arguments on the command line. (A pipe puts content on stdin, not the command line, which is a different matter). In that case, usage would be ./myscript * to pass all files in the current directory. Commented May 11, 2015 at 20:19

2 Answers 2

4

xargs is the utility to use for converting stdin input to command-line arguments.

Naively, you could do the following:

ls | xargs ./myscript.sh

However, that will not work as expected with filenames that have embedded spaces, as they will be split into multiple arguments.
Note that ./myscript.sh $(ls) has the same problem.

If your xargs implementation supports the nonstandard -0 option for parsing NUL-separated input, you can fix this as follows:

printf '%s\0' * | xargs -0 ./myscript.sh

Otherwise, use the following, which, however, will only work if no filenames have embedded " characters (or embedded newlines):

printf '"%s" ' * | xargs ./myscript.sh

Since your stdin input comes from filenames in this case, you can use find, which essentially has xargs functionality built in:

find . -type f -maxdepth 1 -exec ./myscript.sh {} +
  • Note that find . -type f -maxdepth 1 in essence does the same as ls, but there are subtle differences, notably that each matching filename will be prefixed with ./ and that the filenames may not be sorted.
  • -exec ./myscript.sh {} + invokes your script with as many filenames ({}) as can fit on a single command line (+) - just like xargs does - which are typically all of them.

Note that both xargs and find ... -exec ... + could result in multiple invocations of the specified command, if not all arguments fit on a single command line.
However, given how long command lines are allowed to be on modern platforms, this will rarely happen - it only happens if you have a huge number of files in your directory and/or their names are really long.

Sign up to request clarification or add additional context in comments.

Comments

1

You can run a command like this:

./myscript.sh $(ls)

This will prompt the user for the some variable, and grep through the results of ls for files that contain the value entered by the user.

However, you are likely to run into problems with this approach, since spaces and other characters can cause your for loop to receive invalid input.

With that in mind, you might also want to look into find and xargs to achieve something similar, depending on how complex your script is. For example:

find . -type f -print0 |xargs -0 grep "test"

4 Comments

./myscript.sh $(ls) is tempting, but it'll break with filenames with embedded spaces. xargs -0 is the most robust approach in general, but note that if you're using find anyway, you can make do with find alone, using its -exec primary: find . -type f -exec grep "test" {} +
Right. I approached his question from the angle of "how can I pass arguments as outputs from a command with bash", not the specific implementation a find / grep script. But, I agree with you, which is why I gave him the find / xargs pointer as well, to point him in the right direction.
Please don't recommend use of an unquoted command substitution as a command-line argument as a general-purpose solution for "how can I pass arguments as outputs from a command with bash", because it is not. If you still want to recommend it, please make it very clear in your answer that it'll only work if (a) the logical output tokens have no embedded whitespace (because token boundaries will be lost due to word splitting), and (b) if the output tokens do not happen to look like globs, because pathname expansion will occur (try echo $(echo '*'), for instance).
Thanks for the feedback. I edited my answer to provide warnings as you recommended.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.