【问题标题】:How to preserve whitespace when saving command output to a Makefile variable?将命令输出保存到 Makefile 变量时如何保留空格?
【发布时间】:2019-01-07 01:06:11
【问题描述】:

我知道像this one 这样的问题,但我的问题是关于makefile,特别是gnumake。

我有一个命令可以将换行符和其他空格打印到标准输出。我想将该输出捕获到一个变量中,然后将该变量打印到一个文件中。

示例生成文件:

OUTPUT=${shell cowsay hello}

all:
    @echo "$(OUTPUT)" > output.txt

运行make后,output.txt包含:

 _______  < hello >  -------          \   ^__^          \  (oo)\_______             (__)\       )\/\                 ||----w |                 ||     ||

我希望它保留空格,而是包含:

 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

我实际使用的命令不是cowsay,但它输出类似的空格和换行符。

【问题讨论】:

    标签: makefile whitespace gnu-make


    【解决方案1】:

    我想将该输出捕获到一个变量中,然后打印 变量到文件。

    似乎没有办法绕过make 将shell 命令输出中的换行符转换为空格的机制。与原始方法保持一致的一点技巧是在将输出分配给变量时让 shell 将换行符转换为一些不常见的字符(如\1),然后在echo-ing 时将其转换回来该变量到文件中。像这样的:

    OUTPUT=$(shell cowsay hello | tr '\n' '\1')
    
    all:
            @echo "$(OUTPUT)" | tr '\1' '\n' > output.txt
    

    对我来说,这会导致

    $ make
    $ cat output.txt 
     _______ 
    < hello >
     ------- 
            \   ^__^
             \  (oo)\_______
                (__)\       )\/\
                    ||----w |
                    ||     ||
    

    【讨论】:

    • 丑陋但令人印象深刻。正确的解决方案可能是不将输出存储在 Make 变量中。
    【解决方案2】:

    不幸的是,这是一个否定的答案。 GNU Make Manual states(强调):

    shell 函数执行与反引号在大多数 shell 中执行的相同功能:它执行 命令扩展。这意味着它将 shell 命令作为参数并计算命令的输出。 make 对结果的唯一处理是将每个换行符(或回车符/换行符对)转换为单个空格。如果有尾随(回车和)换行符,它将被简单地删除。

    我认为evaldefine 不会有帮助。

    我能看到的唯一解决方法是将cowsay hello command 存储在一个变量中,而不是命令的输出中,如下所示:

    OUTPUT=$$(seq 5)     # seq 5 replaces cowsay hello
    
    all:
        @echo "$(OUTPUT)" > output.txt
    

    如果cowsay hello 的计算成本很高或有副作用,则此解决方法很糟糕。第二种解决方法是保留其输出,但用任意字符替换换行符,例如使用sed,然后添加换行符。例如:

    OUTPUT=$(shell seq 5 | sed s/$$/xxx/)            # add xxx to the end of each line
    
    all:
        @echo -n $(OUTPUT) ' ' | sed 's/xxx /\n/g'   # replace xxx-space with line break
    

    【讨论】:

    • 另一个简单的解决方案是首先将输出存储在一个文件中。
    • 编辑: Reinier Torenbeek 的answer 中也使用了替换思想。他们的答案可能比我的更优雅,使用tr 而不是sed
    【解决方案3】:

    Base64 编码

    我建议你将cowsay的输出编码为base64:

    $ cowsay hello
    
     _______ 
    < hello >
     ------- 
            \   ^__^
             \  (oo)\_______
                (__)\       )\/\
                    ||----w |
                    ||     ||
    
    
    $ cowsay hello | base64 -w0
    IF9fX19fX18gCjwgaGVsbG8gPgogLS0tLS0tLSAKICAgICAgICBcICAgXl9fXgogICAgICAgICBcICAob28pXF9fX19fX18KICAgICAgICAgICAgKF9fKVwgICAgICAgKVwvXAogICAgICAgICAgICAgICAgfHwtLS0tdyB8CiAgICAgICAgICAgICAgICB8fCAgICAgfHwK
    

    生成文件

    cow:
        @echo IF9fX19fX18gCjwgaGV.... | base64 -d
    

    make cow

    现在只需运行make cow 即可正确显示(没有错误的换行符)

    $ make cow
    
     _______ 
    < hello >
     ------- 
            \   ^__^
             \  (oo)\_______
                (__)\       )\/\
                    ||----w |
                    ||     ||
    

    【讨论】:

      【解决方案4】:

      由于$(shell) 去掉了换行符,只需将它们替换为另一个字符或模式即可。回到 make 后,将该字符替换为换行符。

      这里我选择了: 作为我的换行符。 YMMV

      define \n
      
      
      endef
      
      cow := $(subst :,${\n},$(shell cowsay hello | tr \\n :))
      
      $(error [${cow}])
      

      给予

      $ make
      Makefile:7: *** [ _______
      < hello >
       -------
              \   ^__^
               \  (oo)\_______
                  (__)\       )\/\
                      ||----w |
                      ||     ||
      ].  Stop.
      

      你会发现在食谱中使用 ${cow} 很棘手!

      【讨论】:

        猜你喜欢
        • 2014-04-01
        • 2011-12-31
        • 2010-12-24
        • 2014-04-18
        • 1970-01-01
        • 1970-01-01
        • 2018-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多