0

To cut the chase to the point, the following (originating from a Charles Duffy's reply to a related question - see his answer) would be an acceptable format for my requirements:

eval "$(
  { sed -E -e 's/^[ ]+//;' -e ':a;N;$!ba;s/\n//g' | tr -d '\n'; } <<'____COMMAND'
sshfs 
foo_user@fooserver.com:/sftp_folder 
  /var/sshfs.sandbox/server.com 
  -o 
    user=foo_user
    ,reconnect
    ,ServerAliveInterval=15
    ,ServerAliveCountMax=3
____COMMAND
)"

Note that there is a trailing space whenever it is needed by the command I'm trying to split into multple lines:

sshfs 
foo_user@fooserver.com:/sftp_folder 
  /var/sshfs.sandbox/server.com 
  -o 

And no trailing spaces for options that have to be concatenated without an intervening blank space:

user=foo_user
,reconnect
,ServerAliveInterval=15
,ServerAliveCountMax=3

Once concatenated, the command becomes:

sshfs foo_user@fooserver.com:/sftp_folder /var/sshfs.sandbox/server.com -o user=foo_user,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3

However, I am told trailing spaces are a bad idea all around (once again, refer to Charles Duffy's reply for the motivations)

Hence I am wondering. Is there a way to achieve all these three:

  1. Keeping the statement all "together".
  2. Not having trailing whitespace.
  3. Making the "header" and "footer" fixed and copy pastable in other scripts without modifications.

I was thinking of using a trailing backslash and modifying the parsing expression:

{ sed -E -e 's/^[ ]+//;' -e ':a;N;$!ba;s/\n//g' | tr -d '\n'; }

so that the I can achieve all three mentioned above.

Community
  • 1
  • 1
lucid_dreamer
  • 362
  • 4
  • 9

2 Answers2

0

Have you considered using a multiline command with backslashes? This makes the trailing space clearly visible.

From the Bash manual :

3.1.2.1 Escape Character

A non-quoted backslash ‘\’ is the Bash escape character. It preserves the literal value of the next character that follows, with the exception of newline. If a \newline pair appears, and the backslash itself is not quoted, the \newline is treated as a line continuation (that is, it is removed from the input stream and effectively ignored).

sshfs \
foo_user@fooserver.com:/sftp_folder \
/var/sshfs.sandbox/server.com \
-o \
user=foo_user\
,reconnect\
,ServerAliveInterval=15\
,ServerAliveCountMax=3,\

The backslash/newline trick is part of the normal parsing of commands and has no other effects than ignoring newlines, so you can use quotes the way you would normally do.

Maybe a tad less clean in terms of expressing the command itself, but does not require a here document or an additional call to sed.

This being said, CharlesDuffy recommended an array-based solution in an answer to your original question, and putting commands and arguments in an array is, in my experience, a simple and effective solution to many headaches. Is there a reason you do not want to use this method?

Fred
  • 6,590
  • 9
  • 20
  • You solution will insert spaces, where none there should be (i.e. "user=foo_user,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3"). – lucid_dreamer Apr 22 '17 at 03:50
  • "Is there a reason you do not want to use this method?" : I want to enter the command in one place only, I like it that way. – lucid_dreamer Apr 22 '17 at 03:51
  • You are right, I forgot to remove the leading spaces. This is fixed. As for entering the command in one place only, why does this method not allow you to do that? It can be put in a script in a single place. – Fred Apr 22 '17 at 10:15
  • I want to preserve the variable leading space to aid in formatting the command. Your solution flattens the indentation. I want to enter the command with the indentation I've specified in my initial question. – lucid_dreamer Apr 23 '17 at 07:56
  • Please see my other answer. I am not sure your goal is a goal that should be pursued (I obviously do not know what your use case is, so I cannot make an informed judgement on that), but hopefully this other answer has an approach that brings you closer to what you want. – Fred Apr 23 '17 at 10:38
0

Hopefully, this will do what you want.

First, define a function intended to parse a command received on standard input.

#!/bin/bash

execute_command()
{
  local -a command=()
  local arg=
  while IFS= read -r line
  do
    [[ $line =~ [[:blank:]]*(.*[^[:blank:]\\])(\\?)[[:blank:]]*$ ]]
    if
      [[ ${BASH_REMATCH[2]} ]]
    then
      arg+=${BASH_REMATCH[1]}
    else
      command+=("$arg${BASH_REMATCH[1]}")
      arg=
    fi
  done
  "${command[@]}"
}

Then, call this function by leaving a trailing slash at the end of each line where you want the following line to be considered part of the same argument. The above code will not perform any parsing, so you cannot put two arguments on the same line (which, in your example, you are not doing, and I therefore assume is an acceptable limitation for you).

execute_command <<'____COMMAND'
sshfs
foo_user@fooserver.com:/sftp_folder
  /var/sshfs.sandbox/server.com
  -o
    user=foo_user\
    ,reconnect\
    ,ServerAliveInterval=15\
    ,ServerAliveCountMax=3
____COMMAND

This provides you with a very simple, reusable "header", does not require (but tolerates) trailing spaces and allows leading spaces on all lines, while introducing as little heavy/weird syntax as possible.

Fred
  • 6,590
  • 9
  • 20
  • Interesting take, but I can't just copy paste the header and footer. I will also have to copy-paste the function (or include it in globally executed scripts etc (meaning I can't distribute the script that contains the header/footer easily). – lucid_dreamer Apr 27 '17 at 03:31
  • If you are going to distribute your script outside of an execution environment over which you have control (i.e. ability to have "library" scripts available to source), why do you want to use such a non-standard way of expressing commands? At some point, having the command on one really long line or in consecutive lines but without indentation may, even if not pretty, turn out to be the best trade-off... – Fred Apr 27 '17 at 10:24
  • Why not put options in variables, and then expand them on a (much shorter) line to execute the command? – Fred Apr 27 '17 at 10:24
  • I don't care about it being non-standard; I am worried about having to manage dependencies. – lucid_dreamer Apr 27 '17 at 14:45
  • "Why not put options in variables, and then expand them on a (much shorter) line to execute the command?" That's already been suggested in the other post, but I stated clearly that I don't want to split the command. – lucid_dreamer Apr 27 '17 at 14:49
  • Why not just putting the command on a single line or split with escaped newlines without leading spaces? This does not cause dependency problems, and does not require splitting the command. It has the advantage of being widely understood, whereas a more readable but special-purpose syntax could have people scratching their heads when trying to understand what is going on. I understand it is not what you are asking for, but my question is : why is what you want so important that you are willing to throw away standard shell idioms in order to achieve it? – Fred Apr 27 '17 at 16:03
  • I don't want to give up leading whitespace. It aids my quick comprehension of the command. I'm ok with having trailing characters, as they don't affect said "quick comprehension of the command". – lucid_dreamer Apr 28 '17 at 01:53