使用sed删除前导空格的多行bash:命令参数

时间:2017-04-17 20:30:36

标签: bash heredoc sshfs

我正在运行这段代码:

annotate-output $((sed -E 's/^[ ]+//;'  <<____COMMAND

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

____COMMAND
) | sed -E -e ':a;N;$!ba;s/\n//g')

这是我(也许是笨拙的)尝试在bash中实现多线的通用方法。请注意,只要命令我试图分成多行,就会有一个尾随空格:

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

并且没有必须连接的选项的尾随空格而没有插入空格:

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

如果我在任何地方都没有引号或双引号,它就可以了。如果我运行这个:

annotate-output $((sed -E 's/^[ ]+//;'  <<____COMMAND

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

____COMMAND
) | sed -E -e ':a;N;$!ba;s/\n//g')

我明白了:

fuse: bad mount point `"/var/sshfs.sandbox/server.com"': No such file or directory

看起来引号作为“参数”的一部分传递给sshfs。为什么呢?

编辑1(20170418 0739)[注意这现在是separate question]:

我现在明白为什么保留引号(ty Charles Duffy - 请参阅his answer below

我在this answer之后建模了我的解决方案。我需要两次应用sed,一次用于领先空间,另一次用于EOL,这就是我最终使用这些理解的原因。 PS:我有前导空格,而不是前导标签,因此删除前导标签的&lt;&lt; - 标记无效

我最初的问题是我想要一个可以粘贴在任何脚本中的样板页眉/页脚,然后只需键入此页眉中包含的命令,就像我不必担心缩进一样。 我知道我可以通过分配变量然后稍后使用结果来解决问题,但我不希望这样。我希望能够只在一个地方输入新的,缩进的命令 。 基本上。有这些特点:

  • 保留实际EOL之前的行尾空格
  • 删除了行尾
  • 删除了新行前导空格

这可能吗?

编辑2(20170421 0818):原始问题已得到解答。请按照我正在尝试解决的相关问题的演变here

2 个答案:

答案 0 :(得分:1)

实际工作的方法

IFS=,  # cause "${array[*]}" to combine all items with commas

options=(
  user=foo_user           # can put a comment here if you like
  reconnect               # or here
  ServerAliveInterval=15  # or so forth
  ServerAliveCountMax=3
)

sshfs_cmd=(
  sshfs 
  foo_user@fooserver.com:/sftp_folder 
    /var/sshfs.sandbox/server.com
      -o "${options[*]}"
)

"${sshfs_cmd[@]}"

解释为什么其他方法不

不带引号的扩展会经历以下解析步骤,而以下解析步骤:

  • 字符串拆分(根据IFS中的字符分割为多个不同的字)
  • 全局扩展(用类似于该表达式的名称列表替换任何看起来像glob表达式的单词)。

就是这样。没有引用,没有引用删除,没有参数替换等。如果没有引号被解析为语法,它们将导致被解析为单个单词的变量不会以这种方式进行解析;如果没有引用删除,那些引号将作为文本数据出现在被调用的命令中。

有关详细讨论,请参阅BashFAQ #50

答案 1 :(得分:0)

使用eval

以下是您想要的 - 删除前导空格而不是尾随空格,并将heredoc内容组合到一个命令中。 (没有必要在命令结尾处保留一个尾随的换行符:eval "true"即使没有true以换行符结尾,也能很好地工作。

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
)"

需要注意的一些事项:

  • heredoc(____COMMAND)中的印记是引用的。这样可以确保扩展仅在调用eval之后与之前(当扩展heredoc的内容本身时)之后发生。
  • 可以使用每个sed的调用将多个命令传递给单个-e调用。

但是不要。严重。

为尾随空格赋予语义含义是一种简单的方法来混淆其他读取代码的人。正如标签与空间问题一样,关注使用Makefiles或使用Python的几代开发人员(仅在PEP-8标准化空间之前),使尾随空间与无尾随空间成为语义上有意义的区别将会严重混淆任何阅读您的代码的人,都会导致与自动格式验证冲突(例如当前版本的git中包含的功能,以便在将来任何行包含尾随空格时发出警告)。