If you trust the input, you can use eval
(normally to be avoided, as rogue input strings can do unwanted things):
line="1 \"Some Text Here\""
eval print "$line" # `print` is the shell function from the question
Even with harmless input strings, however, the eval
command will break if the input string contains so-called shell metacharacters: | & ; ( ) < >
.
Also, if the string happens to contain tokens such as *
that look like pathname patterns (globs), they would inadvertently get expanded; the relevant pattern characters are: * ? [ ]
.
Thus, to make the above more robust, escape pattern characters and metacharacters in the input string with \
as follows:
eval print "$(sed 's/[][*?&;()<>]/\\&/g' <<<"$line")"
Update: It turns out that there's no need for eval
, after all: the problem can be solved with xargs
, which recognizes quoted substrings inside a string literal.:
#!/usr/bin/env bash
# Sample function that prints each argument passed to it separately
print() {
for arg; do
echo "[$arg]" # enclose value in [] to better see its boundaries
done
}
# Sample input string with embedded quoted strings, an unqoted
# glob-like string, and an string containing shell metacharacters
line="1 \"double-quoted string\" * 'single-quoted string' string-with-;|>-(metachars)"
# Let `xargs` split the string into lines (-n 1) and read the
# result into a bash array (read -ra).
# This relies on `xargs`' ability to recognize quoted strings embedded
# in a string literal.
IFS=$'\n' read -d '' -ra args <<<"$(xargs -n 1 printf '%s\n' <<<"$line")"
# Now pass the array to the `print` function.
print "${args[@]}"
Result:
[1]
[double-quoted string]
[*]
[single-quoted string]
[string-with-;|>-(metachars)]
Notes and limitations:
Unquoted tokens allow use of
\
to escape embedded characters such as spaces, single and double quotes.- However, an
\
before other characters is also ignored, which can be undesired; e.g.:echo 'a\b' | xargs # -> 'ab'
- either use\\
, or single- or double-quote the token instead.
- However, an
Quoted tokens do not require interior
\
-escaping, but sadly, do not support embedding quotes of the same type - no escaping seems to work.Note that specifying
printf '%s\n'
as the command to execute byxargs
is typically not necessary, becausexargs
defaults to invoking theecho
utility (not the shell builtin); however, someecho
implementations recognize options of their own, such as-e
(while not supporting--
), so that the first output token could be mistaken for an option.
By contrast,printf '%s\n
works in all situations.Quoted strings that have embedded newlines are not supported. (
xargs
reports a parsing error in that case) - presumably, this will rarely be a problem.