Shell What is the best way to implement -y (yes) option for POSIX shell script?

Suppose we have two scripts: one that prompts for the input and one that acts as a wrapper for the first one:

prompt:
sh:
#!/bin/sh
printf "Enter: " 1>&2
read input
echo "You entered: ${input}"

wrapper:
sh:
#!/bin/sh
./prompt

Now imagine that we want to implement the behaviour for the wrapper that is usually implemented as a -y (always yes) option for programs. Just to keep it simple, let's say we want to supply prompt with "y" input if wrapper is called with one or more arguments. The obvious way to do this would be to employ yes(1) and duplicate the code:

wrapper:
sh:
#!/bin/sh

if [ ${#} -gt 0 ]; then
    yes |./prompt
else
    ./prompt
fi

It works, but I don't really like that in this case we have to duplicate the code that invokes prompt. Is there a way to do this without duplication?

Recently I came across some shell script (unfortunately I don't remember what it was and where) where I noticed (I looked through the source code really briefly) that somewhat similar was implemented using file descriptors and io redirections. I.e. finally, the command was executed with a specific file descriptor as an input, and that file descriptor either provided "y" strings or acted exactly as stdin, connected to the terminal. I may be wrong and maybe I totally misunderstood what the code was actually about, but I had an impression that it did that. I thought I would come back and examine the code later, but as I said, I forgot what is was :(

The question may probably be very simple, but I'm really confused for whatever reason.
I'm looking for POSIX shell solutions only.
 
best is relative, but my warm fuzzy place is to iterate thru all command line parameters and use case...esac to check each param, and consume any expected following option in two part parameters, such as (-n 3).

And FWIW, don't wrap something like that. it is sloppy to do so. I'd use the methodology described above and set sentinel vars for options that were provided.
I guess what I'm really saying is "don't write posix scripts that read data the way you mention in your example. Either pass all data on the command line, or have the script iteratively read from stdin (based on a command line option)
 
I would approach it with a file method. If the data is sensitive you can use a FIFO instead (data is gone once consumed). This would require the least amount of modification to "prompt.sh".

sh:
[wrapper-communication]cat script.sh
#!/bin/sh

# Note: no prompt, just setting a var.

fd=$(mktemp -u)
answer="yes"

# write to fd.
echo "$answer" > "$fd"
# return the fd
echo "$fd"
[wrapper-communication]cat wrapper.sh
#!/bin/sh

fd=$(exec ./script.sh)
read ANS < "$fd"

printf -- "The answer is: %s\n" $ANS
[wrapper-communication]./wrapper.sh
The answer is: yes
 
[…] The obvious way to do this would be to […] duplicate the code: […]
The obvious way is to define and use a custom shell function​
Bash:
main() {
	./prompt -a 1 -b 2 -c 3 -d 4
}
so any long parameter list is in one place only.​
sh:
#!/bin/sh
if [ ${#} -gt 0 ]; then
 prefix='yes |'
fi
eval ${prefix} ./prompt
This can be abbreviated even further:​
Bash:
eval ${1:+yes |} exec ./prompt foo bar able
[…] Recently I came across some shell script […] where I noticed […] that somewhat similar was implemented using file descriptors and io redirections. […]
Are you sure this was a bare POSIX™ shell script? JohnK’s implementation works, I guess, but the overhead is awkward, with various feature‑rich shells you can probably solve it more elegantly.​
 
Thanks everyone for the answers!

This can be abbreviated even further: eval ${1:+yes |} exec ./prompt foo bar able
Thanks for that! I knew about :+ expansion, but couldn't find how it can be applied in a real script. Neat :)

JohnK's solution is quite close, but I think I finally implemented what I wanted without any modifications to prompt:

sh:
#!/bin/sh

cleanup()
{
    rm -f "${fifo}"
}
trap cleanup INT TERM EXIT

exec 3<&0
if [ ${#} -gt 0 ]; then
    fifo=$(mktemp -up /tmp)
    mkfifo "${fifo}"
    yes >"${fifo}" &
    exec 3<"${fifo}"
fi
./prompt <&3
kill 0
 
Back
Top