How do I use the lines of a file as arguments of a command?
Solution 1
If your shell is bash (amongst others), a shortcut for $(cat afile)
is $(< afile)
, so you'd write:
mycommand "$(< file.txt)"
Documented in the bash man page in the 'Command Substitution' section.
Alterately, have your command read from stdin, so: mycommand < file.txt
Solution 2
As already mentioned, you can use the backticks or $(cat filename)
.
What was not mentioned, and I think is important to note, is that you must remember that the shell will break apart the contents of that file according to whitespace, giving each "word" it finds to your command as an argument. And while you may be able to enclose a command-line argument in quotes so that it can contain whitespace, escape sequences, etc., reading from the file will not do the same thing. For example, if your file contains:
a "b c" d
the arguments you will get are:
a
"b
c"
d
If you want to pull each line as an argument, use the while/read/do construct:
while read i ; do command_name $i ; done < filename
Solution 3
command `< file`
will pass file contents to the command on stdin, but will strip newlines, meaning you couldn't iterate over each line individually. For that you could write a script with a 'for' loop:
for line in `cat input_file`; do some_command "$line"; done
Or (the multi-line variant):
for line in `cat input_file`
do
some_command "$line"
done
Or (multi-line variant with $()
instead of ``
):
for line in $(cat input_file)
do
some_command "$line"
done
References:
- For loop syntax: https://www.cyberciti.biz/faq/bash-for-loop/
Solution 4
You do that using backticks:
echo World > file.txt
echo Hello `cat file.txt`
Solution 5
If you want to do this in a robust way that works for every possible command line argument (values with spaces, values with newlines, values with literal quote characters, non-printable values, values with glob characters, etc), it gets a bit more interesting.
To write to a file, given an array of arguments:
printf '%s\0' "${arguments[@]}" >file
...replace with "argument one"
, "argument two"
, etc. as appropriate.
To read from that file and use its contents (in bash, ksh93, or another recent shell with arrays):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
To read from that file and use its contents (in a shell without arrays; note that this will overwrite your local command-line argument list, and is thus best done inside of a function, such that you're overwriting the function's arguments and not the global list):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Note that -d
(allowing a different end-of-line delimiter to be used) is a non-POSIX extension, and a shell without arrays may also not support it. Should that be the case, you may need to use a non-shell language to transform the NUL-delimited content into an eval
-safe form:
quoted_list() {
## Works with either Python 2.x or 3.x
python -c '
import sys, pipes, shlex
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))
'
}
eval "set -- $(quoted_list <file)"
run_your_command "$@"
Related videos on Youtube
Yoo
Updated on July 14, 2021Comments
-
Yoo almost 3 years
Say, I have a file
foo.txt
specifyingN
argumentsarg1 arg2 ... argN
which I need to pass to the command
my_command
How do I use the lines of a file as arguments of a command?
-
Charles Duffy about 10 years"arguments", plural, or "argument", single? The accepted answer is correct only in the single-argument case -- which is what the body text inquires about -- but not in the multi-argument case, on which the topic appears to inquire.
-
Navin over 8 yearsThe 4 answers below usually have identical results, yet they have slightly different semantics. Now I remember why I stopped writing bash scripts :P
-
-
Will over 13 yearsI should have mentioned, I am assuming that you are using bash. I realize that there are other shells out there, but almost all of the *nix machines I have worked on either ran bash or some equivalent. IIRC, this syntax should work the same on ksh and zsh.
-
Charles Duffy about 10 yearsRead should be
read -r
unless you want to expand backslash-escape sequences -- and NUL is a safer delimiter to use than the newline, particularly if the arguments you're passing are things like filenames, which can contain literal newlines. Also, without clearing IFS, you get leading and trailing whitespace implicitly cleared fromi
. -
Charles Duffy about 7 yearsYour "nice little trick" is dangerous from a security perspective -- if you have an argument containing
$(somecommand)
, you'll get that command executed rather than passed through as text. Likewise,>/etc/passwd
will be processed as a redirection and overwrite/etc/passwd
(if run with appropriate permissions), etc. -
Charles Duffy about 7 yearsIt's much safer to do the following instead (on a system with GNU extensions):
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
-- and also more efficient, since it passes as many arguments to each shell as possible rather than starting one shell per line in your input file. -
Charles Duffy about 7 yearsAlso, putting
< file
in backticks means it doesn't actually perform a redirection forcommand
at all. -
Charles Duffy almost 7 yearsThis doesn't create an argument -- because it isn't quoted, it's subject to string-splitting, so if you emitted
echo "Hello * Starry * World" > file.txt
in the first step, you'd get at least four separate arguments passed to the second command -- and likely more, as the*
s would expand to the names of files present in the current directory. -
Charles Duffy almost 7 years(Did the people who upvoted this actually test it?
command `< file`
doesn't work in either bash or POSIX sh; zsh is a different matter, but that's not the shell this question is about). -
Charles Duffy almost 7 years...and because it's running glob expansion, it doesn't emit exactly what's in the file. And because it's performing a command substitution operation, it's extremely inefficient -- it's actually
fork()
ing off a subshell with a FIFO attached to its stdout, then invoking/bin/cat
as a child of that subshell, then reading the output through the FIFO; compare to$(<file.txt)
, which reads the file's contents into bash directly with no subshells or FIFOs involved. -
Charles Duffy almost 7 yearsThis is -- but for being substantially less efficient -- effectively equivalent to
$(<bar.txt)
(when using a shell, such as bash, with the appropriate extension).$(<foo)
is a special case: unlike regular command substitution, as used in$(cat ...)
, it doesn't fork off a subshell to operate in, and thus avoids a great deal of overhead. -
lstyls over 6 yearsTo be pedantic, it's not a shortcut to use the
<
operator. It means that the shell itself will perform the redirection rather than executing thecat
binary for it's redirection properties. -
becko over 6 yearsIs there a limit to the number of arguments? If so, how can I overcome it?
-
glenn jackman over 6 yearsIt's a kernel setting so you'd need to recompile the kernel. Or investigate the xargs command
-
mwfearnley about 6 years@CharlesDuffy Can you be more specific about how it doesn't work?
-
ykaner almost 6 yearsCan you explain to me why I need the
$
here? -
glenn jackman almost 6 yearsBecause it is required by the bash syntax as documented in the manual: Command Substitution
-
törzsmókus over 5 years@ykaner because without
"$(…)"
, the contents offile.txt
would be passed to the standard input ofmycommand
, not as an argument."$(…)"
means run the command and give back the output as a string; here the “command” only reads the file but it could be more complex. -
Pete almost 5 yearsIt's not working for me unless I lose the quotes. With the quotes, it takes the whole file as 1 argument. Without quotes, it interprets each line as a separate arg.
-
glenn jackman almost 5 yearsWithout the quotes, it takes each word in the file as a separate arg, plus each word is subject to globbing - if one of the words is
*
then that expands to all the filenames in the current directory. -
tripleee almost 5 yearsAnd of course, lose the useless use of
cat
-
Arnie97 almost 5 years@CharlesDuffy This does work in bash 3.2 and zsh 5.3. This won't work in sh, ash and dash, but neither $(< file).
-
Charles Duffy almost 5 years@mwfearnley, happy to provide that specificity. The goal is to iterate over lines, right? Put
Hello World
on one line. You'll see it first runsome_command Hello
, thensome_command World
, notsome_command "Hello World"
orsome_command Hello World
. -
LarsH over 4 years@lstyls: I'm not sure what you mean by "it's not a shortcut." Having the shell perform the redirection instead of spawning a child
cat
process sounds like it would save resources. The ref man describes this substitution as "equivalent but faster." Maybe you mean "it's not just a shorter way of typing the same thing"? -
lstyls over 4 years@LarsH you’re absolutely correct. That’s a less confusing way to put it, thanks.
-
glenn jackman over 4 yearsSo perhaps "a builtin alternative for" would be more accurate.
-
Gabriel Staples over 4 yearsThe for loop option here works great! I just added some multi-line variants of it to help the next person. Tested it with calling
git add
on a bunch of lines to add that were stored in a file. -
Gabriel Staples over 4 yearsActually...it doesn't properly parse lines which have spaces in them. So, instead of editing this answer yet again and completely corrupting it from the author's original answer, I have added my own answer here! I like it the best of all now. It's completely borrowed from another site (and cited) though anyway. Here's my answer: stackoverflow.com/questions/4227994/….