Bash Question

Home » CentOS » Bash Question
CentOS 4 Comments

Hi All,

I am trying to build a command line with spaces in the argument. This demonstrates what I am trying to do. Clearly the first two commands work fine. However, the last 4 lines to not.

/opt/libreoffice5.4/program/soffice.bin –headless –convert-to csv
“/tmp/file.xlsx”
/opt/libreoffice5.4/program/soffice.bin –headless –convert-to csv
“/tmp/file 2.xlsx”

MSG=”file 2″
MSG=”csv \”$MSG\””
echo $MSG
/opt/libreoffice5.4/program/soffice.bin –headless –convert-to $MSG

I am trying to make a variable containing spaces which is MSG. Then add to that variable the argument csv. The “echo” above prints the write stuff. But when I try to use it in the last command its no longer valid and says Source file could not be loaded.

What am I missing? much searching hinted to arrays and using \$ but I
could not get that to work.

Thanks.

Jerry

4 thoughts on - Bash Question

  • Jerry Geis wrote:

    I think the second MSG= effectively overwrites the first assignment. Why not use FIL=”file 2″;MSG=”csv $FIL”? Note, also, you don’t need quotes or backslash escapes – as long as you use quotes, not apostrophes, it will be interpreted.

    mark

  • Using arrays to build the command is useful because the arguments are absolutely defined in each element of the array – i.e. the arguments to the exec call are correctly built.

    I think the simplest way of doing what you want on a command line is to use two separate variables:

    MSG1=”file 2″
    MSG2=”csv”
    /opt/libreoffice5.4/program/soffice.bin –headless –convert-to $MSG2 “$MSG1”

    P.

  • This is a really convoluted way of doing things, and you’d have to be a super expert in quoting to get this right. Instead, why don’t you just have 2 variables, and pass them both, eg:

    MSG=csv FILENAME=”my file with spaces”
    /path/to/soffice.bin –headless –convert-to “$MSG” “$FILENAME”

    Remember to quote both variables, so that if they have any spaces, the spaces are not accidentally seen as parameter separators, and instead get passed to the soffice.bin program.

    Regards, Anand

  • I’d describe what you’re trying to do as “using one shell variable to expand to two and only two arguments in a shell simple command.” And, as others have told you, you can’t do that.  The reason why is that shell parsing isn’t recursive.

    When the shell expands a simple command, it first parses the command in to words or tokens.  This step is influenced by quoting.  So this command:
      soffice –convert-to csv “file 2”
    …becomes these tokens:
      [ ‘soffice’, ‘–convert0-to’, ‘csv’, ‘file 2’ ]
    Quote characters which influence token parsing are removed in this step.  Variable substitution happens after token parsing.  So, this command:
      soffice –convert-to csv $msg
    …becomes these tokens:
      [ ‘soffice’, ‘–convert-to’, ‘$msg’ ]
    …which become to these tokens after variable substitution:
      [ ‘soffice’, ‘–convert-to’, ‘csv’, ‘”file’, ‘2″‘]
    And this is where shell grammar gets a little confusing, because the shell doesn’t parse the variable expansion according to the token rules, but it does perform “field splitting” on an unquoted variable, and that kind of looks like the same thing.  It’s not. You can’t influence the expansion of the variable through quoting in its content, nor can you embed variable names or other substitutions.

    This stuff is detailed here:

    http://pubs.opengroup.org/onlinepubs/7908799/xcu/chap2.html

    The easy solution is to use multiple variables.  The slightly more complex solution is to use an array (which is supported by bash, but not POSIX shell, so it’s not referenced in the above document).  The really ugly solution is to use “eval”:

    msg=”csv \”file 2\””
    eval “soffice –convert-to $msg”

    Don’t use eval in any language, if you can avoid it.  In non-trivial uses, it becomes difficult to predict all possible expansions, and can create variable injection attacks.