0

I have a shell script (I use GNU bash 4.4.20 on Ubuntu 18.04) with a list of commands I often run, e.g.:

sudo program1 $1 foo arg1
sudo program2 $1 foo arg2
sudo program1 $1 bar arg3
sudo program2 $1 bar arg4

I'd like to grep for select lines in this script and run them. For example if I'd like to run lines 3 and 4 above with $1 set to "some_value", I was thinking something like this would work:

set some_value; grep bar my_shell_script.sh | xargs -L1

I think my problems are 1) I'm not sure how to use xargs if the command itself is embedded in the input and 2) I'm not sure how to set $1 in the context of xargs if it's receiving its input from grep.

nonagon
  • 553
  • 4
  • 5

1 Answers1

1

In the lines of what you tried:

grep bar my_shell_script.sh | bash -- /dev/stdin some_value

Note in the above code the stdin of the new bash comes from grep. If the new bash runs a command that also reads from stdin then the command will read from grep. To be on the safe side or if you explicitly want to use the stdin for something else, use this syntax:

bash <(grep bar my_shell_script.sh) my-bash some_value
# or
bash -c "$(grep bar my_shell_script.sh)" my-bash some_value

Or if you want the code to be executed in the current shell:

. <(grep bar my_shell_script.sh) some_value

Notes:

  • Don't you want to quote $1 in your file?
  • Process substitution (<(…)) is not portable. You tagged , Bash supports it. Command substitution ($(…)) is portable but it will fail with argument list too long if the output from grep is too long.
  • my-bash is explained here: What is the second sh in sh -c 'some shell code' sh? (the explanation is in the context of sh -c/bash -c).
  • /dev/stdin may or may not exist in your OS.
  • This answer does not discuss (in)sanity of running code by filtering a "script" with grep. In this matter I assume you know what you want.
Kamil Maciorowski
  • 69,815
  • 22
  • 136
  • 202
  • This works great. Agreed about quoting $1 in reality, I just made up that example for brevity. Also agreed that grepping commands out of a script is somewhat insane - in my case I think it makes sense but it'd require way too much context to get 2nd opinions on that. I was surprised that xargs doesn't have a mode that takes the command from its input! I guess there's security considerations in allowing that. – nonagon Apr 27 '21 at 00:10
  • 1
    @nonagon IMO it's not about security. `xargs` is designed to run some executable with arguments taken from stdin. I mean the name of the executable *does not* come from stdin, it's *fixed* when `xargs` starts and this is *by design*. You can circumvent this with `env` being the executable (`… | xargs -L1 env --`). But to expand `$1` (or like) you need a shell anyway. The basic task of `xargs` is word splitting, a shell can do this as well. If your file is "a *shell* script" then it should be interpreted by a shell. – Kamil Maciorowski Apr 28 '21 at 07:05