Question

I have a line which I have read from a file

line="1 \"Some Text Here\""

and a function which takes two arguments

print() {
    echo $1
    echo $2
}

When I execute

print $line

The output I get is

1
"Some

What I want to get is

1
Some Text Here
Was it helpful?

Solution

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.
  • 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 by xargs is typically not necessary, because xargs defaults to invoking the echo utility (not the shell builtin); however, some echo 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.

OTHER TIPS

You need to use the variable IFS. In your script type IFS=\". This should fix your problem .

Depending on your specific situation, using shift might work for you.

For example:

print() {
    echo "$1"
    shift
    echo "$*"
}

This prints the first parameter, shift the parameter list (removing the first parameter), then prints the remaining parameters.

That should give you this result:

$ text="1 \"Multiple Words Here\""

$ print() {
>     echo "$1"
>     shift
>     echo "$*"
> }


$ print $text
1
"Multiple Words Here"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top