我该如何在bash中分割一个极长的字符串字面量?

64

我希望在bash脚本中嵌入这样一个长命令:

mycommand \
    --server myserver \
    --filename extremely/long/file/name/that/i/would/like/to/be/able/to/break/up/if/possible \
    --otherflag \
    --anotherflag

将长文件名分开。

我可以这样做:

# Insufficiently pretty
mycommand \
    --server myserver \
    --filename extremely/long/file/name/\
that/i/would/like/to/be/able/to/break/\
up/if/possible \
    --otherflag \
    --anotherflag \

但这会破坏整个流程。我希望能够像这样书写:

但它会破坏整个流程。我希望能够这样写:

# Doesn't work
mycommand \
    --server myserver \
    --filename extremely/long/file/name/\
         that/i/would/like/to/be/able/to/break/\
         up/if/possible \
    --otherflag \
    --anotherflag

但这样做不起作用,因为它会打断字符串字面量。

有没有办法告诉bash打破字符串字面量,但忽略任何前导空格?

7个回答

85

这有点像是一种hack方法,但它却确实有效:

mycommand \
    --server myserver \
    --filename "extremely/long/file/name/"`
               `"that/i/would/like/to/be/able/to/break/"`
               `"up/if/possible" \
    --otherflag \
    --anotherflag

Bash会将相邻的字符串字面值连接起来,因此我们利用了这一点。例如,echo "hi" "there" 将打印出 hi there,而 echo "hi""there" 将打印出 hithere

它还利用了反引号运算符以及一堆空格等价于没有的事实。


2
你可以把反引号(\``)放在前一行的末尾,而不是使用反斜杠(​\​`)进行换行…… 这样可以保持左侧的整齐。 - Peter.O
@Peter.O 我不明白你在说什么,请澄清一下。 - Stefan
一个清理你代码的肮脏技巧 - Casey Jones
1
我甚至认为你不需要关闭和重新打开双引号。这对我有效:"one\ [换行] [空格] `two"->"onetwo"` - OLEGSHA

71

你可以使用一个变量:

file=extremely/long/file/name
file+=/that/i/would/like/to/be/able/to/break
file+=/up/if/possible

mycommand\
    --server myserver\
    --filename $file\
    --flag flag

8
好主意。你可以更加简洁,使用 += 运算符代替 file=${file}/... - Chriszuma
5
我认为这是最清晰明了的方法。意图是明确的。 - Oscar Korz
@Chriszuma 是的,我忘记了bash允许使用+=操作符。我已编辑了我的回答。 - WilQu
寻找解决方案已经有一段时间了,但是所有的方法都存在一些小问题,比如增加额外的空格。谢谢。 - zhihong

5

在保持最大行长的情况下,将长字符串写入变量的另一种方法:

printf -v fname '%s' \
    'extremely/long/file/name/that/i/' \
    'would/like/to/be/able/to/break/up/' \
    'if/possible'

由于参数数量超过了格式化指令,%s 将会被重复使用,从而得到以下结果

$ declare -p fname
declare -- fname="extremely/long/file/name/that/i/would/like/to/be/able/to/break/up/if/possible"

可以像这样使用

mycommand \
    --server myserver \
    --filename "$fname" \
    --otherflag \
    --anotherflag

当设置具有本质上分离内容的长变量时,例如CDPATH(或者当然也包括PATH)时,这将非常方便:

printf -v CDPATH '%s' \
    ':/Users/benjamin/here/is/a/long/path' \
    ':/Users/benjamin/and/here/is/another/one' \
    ':/Users/benjamin/and/a/third/line'
export CDPATH

与...相反

export CDPATH=':/Users/benjamin/here/is/a/long/path:/Users/benjamin/and/here/is/another/one:/Users/benjamin/and/a/third/line'

或者说笨重的

export CDPATH=':/Users/benjamin/here/is/a/long/path'
CDPATH+=':/Users/benjamin/and/here/is/another/one'
CDPATH+=':/Users/benjamin/and/a/third/line'

3

也可以使用数组变量

file=(extremely/long/file/name
    /that/i/would/like/to/be/able/to/break
    /up/if/possible)
IFS=''

echo mycommand\
    --server myserver\
    --filename "${file[*]}"\
    --flag flag

2
基本上,bash 没有内置这个功能。
包装器通常比它的价值更麻烦,但是可以尝试使用别名或函数,例如:j
j(){sed -e ':a;$!N;s/ *\n *//g;ta' <<<"$1"}

echo "$(j "3   spaces  
           /hello
           /world
           /this
           /is
           /a
           /long
           /path
          ")"

# 3   spaces/hello/world/this/is/a/long/path

2

我在我的bash脚本顶部定义了一个简短的strcat函数,并使用内联调用来分割内容。有时候我更喜欢这种方式而不是使用单独的变量,因为我可以将长文字直接与命令调用内联定义。

function strcat() {
  local IFS=""
  echo -n "$*"
}

mycommand \
  --server myserver \
  --filename "$(strcat \
      extremely/long/file/name/ \
      that/i/would/like/to/be/able/to/break/ \
      up/if/possible)" \
  --otherflag \
  --anotherflag \

我也喜欢这种方法,特别是在输入一长串CSV值作为标志参数时,因为我可以使用它来避免在值之间键入逗号:

function strjoin() {
  local IFS="$1"
  shift
  echo -n "$*"
}

csv_args=(
  foo=hello
  bar=world
  "this=arg  has  spaces  in  it"
)
mycommand \
  --server myserver \
  --csv_args "$(strjoin , "${csv_args[@]}")" \
  --otherflag \
  --anotherflag \

这相当于

mycommand \
  --server myserver \
  --csv_args "foo=hello,bar=world,this=arg  has  spaces  in  it" \
  --otherflag \
  --anotherflag \

0

顺便提一下,可以使用 heredoc

command=$(cat << EOF
This
    is
    "a command"
    "that spans"
        multiple
        lines
EOF
)
command=$(echo $command)
echo $command

这导致

This is "a command" "that spans" multiple lines

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接