In bash
, you should use an array to hold the paths as read from the user. In general, it's better to keep separate strings (pathnames) separate rather than to concatenate them into a single string that you later have to correctly parse to extract the original constituent strings.
#!/bin/bash
echo 'Enter paths, one by one followed by Enter. End input with Ctrl+D' >&2
mypaths=()
while IFS= read -r -p 'Path: ' thepath; do
mypaths+=( -v "$thepath:/opt/$thepath" )
done
docker run "${mypaths[@]}" fedora
Here, the user is prompted to input a path several times until they press Ctrl+D. The paths entered are saved in the mypaths
array, which is laid out in such a way that docker
may use it directly.
Once there are no more paths to read, the docker
command is called. The "${mypaths[@]}"
will be expanded to the individually quoted elements of the mypaths
array. Since the entries of the array are stored the way they are (with -v
as a separate element before each specially formatted pathname:/opt/pathname
string), this will be correctly interpreted by the shell and by docker
. The only characters that will not be tolerated in pathnames by the above code are newlines, since these are separating the lines read by read
.
The above script would also accept input redirected from a text file containing a single path per line of input.
Note that the quoting is important. Without the double quotes around the variable expansions, you would not be able to use paths containing whitespace, and you would potentially also have issues with paths containing characters special to the shell.
Related:
For non-bash
(sh
) shells:
#!/bin/sh
echo 'Enter paths, one by one followed by Enter. End input with Ctrl+D' >&2
set --
while printf 'Path: ' >&2 && IFS= read -r thepath; do
set -- "$@" -v "$thepath:/opt/$thepath"
done
docker run "$@" fedora
Here we use the list of positional parameters instead of an array (since arrays other than $@
are not available in sh
in general), but the workflow is otherwise identical apart from printing the prompt explicitly with printf
.
Implementing the suggestion at the end of Stéphane Chazelas' comment, so that the script takes pathnames on its command line instead of reading them from its standard input. This allows the user to pass arbitrary pathnames to the script, even those that read
can't easily read or a user can't easily type on a keyboard.
For bash
using an array:
#!/bin/bash
for pathname do
mypaths+=( -v "$pathname:/opt/$pathname" )
done
docker run "${mypaths[@]}" fedora
For sh
using the list of positional parameters:
#!/bin/sh
for pathname do
shift
set -- "$@" -v "$pathname:/opt/$pathname"
done
docker run "$@" fedora
Both of these would be run like
./script.sh path1 path2 path3 ...
bash
'sread -e
where the user can use completion, that will still not allow users to enter paths with newlines (you'd needzsh
'svared
). Best would be to take the list of files from the arguments of the script, where the user can use the quoting or completion features of their shell to pass the arbitrary file names. – Stéphane Chazelas Sep 01 '18 at 08:42