0

I'd like to write a script that reads a file and passes every line as options (or "option arguments") to a command, like this:

command -o "1st line" -o "2nd line" ... -o "last line" args

What's the simplest way of doing this?

jacek
  • 21
  • 2
  • 3
    Possible duplicate of [How to pass each line of a text file as an argument to a command?](https://unix.stackexchange.com/questions/149726/how-to-pass-each-line-of-a-text-file-as-an-argument-to-a-command) – jasonwryan Nov 08 '17 at 20:12
  • 2
    No, it's not, sorry – jacek Nov 08 '17 at 20:12
  • I'd modify the program to accept just the file name/full path as input and have it read the file in itself.... – ivanivan Nov 08 '17 at 22:44

2 Answers2

5
# step 1, read the lines of the file into a shell array
mapfile -t lines < filename

# build up the command
cmd_ary=( command_name )
for elem in "${lines[@]}"; do
    cmd_ary+=( -o "$elem" )
done
cmd_ary+=( other args here )

# invoke the command
"${cmd_ary[@]}"
glenn jackman
  • 84,176
  • 15
  • 116
  • 168
  • This. Although you'll want to maybe check the file for total number of lines (or other delimiter) so that you don't call your command with more arguments than it can handle - might want to read up on this (old) article and then check for newer references - https://www.in-ulm.de/~mascheck/various/argmax/ – ivanivan Nov 08 '17 at 22:43
0

Here is one possibility:

$ cat tmp
1st line
2nd line
3rd line
4th line
$ command $(sed 's|.*|-o "&"|' tmp | tr '\n' ' ')

As glennjackman points out in the comments, word splitting can be circumvented by wrapping in eval, though the security implications of doing so should be appreciated:

$ eval "command $(sed 's|.*|-o "&"|' tmp | tr '\n' ' ')"

Edit: Combining my suggestion of using sed to assemble arguments with glenn jackman's mapfile/readarray approach gives the following concise form:

$ mapfile -t args < <(sed 's|.*|-o\n&|' tmp) && command "${args[@]}"

As a trivial demonstration, consider the aforementioned tmp file, the command grep, and the file text:

$ cat text
some text 1st line and
a 2nd nonmatching line
some more text 3rd line end
$ mapfile -t args < <(sed 's|.*|-e\n&|' tmp) && grep "${args[@]}" text
some text 1st line and
some more text 3rd line end
$ printf "%s\n" "${args[@]}"
-e
1st line
-e
2nd line
-e
3rd line
-e
4th line
user001
  • 3,598
  • 5
  • 39
  • 54
  • Unfortunately, this will not maintain the lines as whole elements due to the shell's [word splitting](https://www.gnu.org/software/bash/manual/bash.html#Word-Splitting). Try where the command is `printf "%s\n"` -- instead of receiving 8 arguments (`-o` `1st line` ...), this technique will give the command **12 arguments** containing literal quote chars (`-o` `"1st` `line"` `-o` `"2nd` `line"` etc). You *could* do `eval "command $(sed ...)"` but that's nasty and fragile. – glenn jackman Nov 08 '17 at 20:41
  • @glennjackman: Agreed, `eval` would be needed to avoid the word splitting problem; your command array-based approach is better for general use. – user001 Nov 08 '17 at 21:05