【问题标题】:Commenting in a Bash script inside a multiline command在多行命令内的 Bash 脚本中进行注释
【发布时间】:2010-11-30 04:05:47
【问题描述】:

如何评论脚本中以下行的每一行?

cat ${MYSQLDUMP} | \
sed '1d' | \
tr ",;" "\n" | \
sed -e 's/[asbi]:[0-9]*[:]*//g' -e '/^[{}]/d' -e 's/""//g' -e '/^"{/d' | \
sed -n -e '/^"/p' -e '/^print_value$/,/^option_id$/p' | \
sed -e '/^option_id/d' -e '/^print_value/d' -e 's/^"\(.*\)"$/\1/' | \
tr "\n" "," | \
sed -e 's/,\([0-9]*-[0-9]*-[0-9]*\)/\n\1/g' -e 's/,$//' | \
sed -e 's/^/"/g' -e 's/$/"/g' -e 's/,/","/g' >> ${CSV}

如果我尝试添加如下评论:

cat ${MYSQLDUMP} | \ # Output MYSQLDUMP File

我明白了:

#: not found

这里可以评论吗?

【问题讨论】:

  • 好吧,正如你所注意到的,如果你先做#,那么\就只是注释的一部分,但如果你先做\,那么后面的字符就会改变它的含义,而不是“行延续”到“引用”。我想到了一种解决方案,如下所示。

标签: bash syntax comments


【解决方案1】:

这是一个 bash 脚本,它结合了之前几个 cmets 的思想和习语,通过示例提供了具有一般形式 ${__:+ <comment text>} 的内联 cmets。

特别是

  • <comment text>可以多行
  • <comment text> 没有参数扩展
  • 不产生子进程(因此 cmets 是高效的)

<comment text> 有一个限制,即不平衡的大括号'}' 和括号')' 必须受到保护(即'\}''\)')。

对本地bash环境有一个要求:

  • 参数名称__必须取消设置

只要名称没有设置值,任何其他语法上有效的 bash 参数名称都将代替 __

下面是一个示例脚本

# provide bash inline comments having the form
#     <code> ${__:+ <comment>} <code> 
#     <code> ${__:+ <multiline
#                   comment>} <code>

# utility routines that obviate "useless use of cat"
function bashcat { printf '%s\n' "$(</dev/stdin)"; }
function scat { 1>&2 bashcat; exit 1; }

# ensure that '__' is unset && remains unset
[[ -z ${__+x} ]] &&  # if '__' is unset
  declare -r __ ||   # then ensure that '__' remains unset 
  scat <<EOF         # else exit with an error
Error: the parameter __='${__}' is set, hence the
  comment-idiom '\${__:+ <comment text>}' will fail
EOF

${__:+ (example of inline comments)
------------------------------------------------
the following inline comment-idiom is supported
    <code> ${__:+ <comment>} <code> 
    <code> ${__:+ <multiline
                   comment>} <code> 
(advisory) the parameter '__' must NOT be set;
  even the null declaration __='' will fail
(advisory) protect unbalanced delimiters \} and \) 
(advisory) NO parameter-expansion of <comment> 
(advisory) NO subprocesses are spawned
(advisory) a functionally equivalent idiom is 
    <code> `# <comment>` <code> 
    <code> `# <multiline
               comment>` <code>
however each comment spawns a bash subprocess
that inelegantly requires ~1ms of computation 
------------------------------------------------}

【讨论】:

【解决方案2】:

而不是你尝试过的:

cat ${MYSQLDUMP} | \ # Output MYSQLDUMP File

其他人提到这应该可行:

cat ${MYSQLDUMP} |   # Output MYSQLDUMP File

由于分割线并不总是以竖线 (|) 结尾,但是,您可以将 cmets 放在自己的行上,如下所示

date && \
  # List current directory
  ls -l | awk '{ \
  # Filename is in the ninth column
      # This is just making "ls -l" work mostly like "ls -1"
  print $9 }'

只是不要在字符串中间这样做:

echo " Hello \
 # Localized name for your planet:
world."

在你的情况下,你可以使用这个方法:

cat ${MYSQLDUMP} | \
# Output MYSQLDUMP File

扩展示例:

# Create .csv file from MySQL dump file
cat ${MYSQLDUMP} |   
# Output MYSQLDUMP File
# and pipe to first sed command
sed '1d' | \
# Pipe output to tr
tr ",;" "\n" | \
# Apply sed expression
sed -e 's/[asbi]:[0-9]*[:]*//g' -e '/^[{}]/d' -e 's/""//g' -e '/^"{/d' | \
    # Apply another two sed expressions
    # (and since whitespace is ignored, you can intent for clarity)
    sed -n -e '/^"/p' -e '/^print_value$/,/^option_id$/p' | \
    # Apply three more sed expressions
    sed -e '/^option_id/d' -e '/^print_value/d' -e 's/^"\(.*\)"$/\1/' | \
    # Use tr to ...
    tr "\n" "," | \
    # Apply yet another two sed expressions
    sed -e 's/,\([0-9]*-[0-9]*-[0-9]*\)/\n\1/g' -e 's/,$//' | \
    # Apply the final three sed expressions
    sed -e 's/^/"/g' -e 's/$/"/g' -e 's/,/","/g' >> ${CSV}

...或混合使用两种方法:

# Create .csv file from MySQL dump file
cat ${MYSQLDUMP} |   # Output MYSQLDUMP File
# and pipe to first sed command
sed '1d' | \
# Pipe output to tr
...

(我相信这两种方法都有效,因为 shell 脚本文件是逐行解析的,CLI 输入也是如此。)

最后的笔记:

  • 重要的是要记住,在使用时,续行字符 (\) 应该是该行中的最后一个字符 (即使是一个被遗忘的尾随空格也会毁了你的夜晚) .

  • 如果从命令行手动输入,如果您打算使用命令历史记录功能,请仅使用第二种方法(每条注释单独一行)

  • 如果使用历史记录并希望保留 cmets,请不要使用这两种方法中的任何一种 - 使用该问题的不同答案中的一种。

【讨论】:

    【解决方案3】:

    我对管道连接命令的首选编码风格是

    command1 \
    | command2 \
    | ...
    

    正如@JimGrisham 和其他人所建议的,注释行的一种方法是

    command1 \
    | # inline comment
      command2 \
    | ...
    

    另一种不调用子shell 的方法是使用 Bash 的 { list; } 构造,该构造始终有效。所以在这里:

    command1 \
    | {
        # inline comment
        command2
      } \
    | ...
    

    【讨论】:

    • 即使删除列表元素也不起作用?
    • @JimGrisham 是的,感谢您指出这一点。我已经相应地改变了我的答案。现在,从技术上讲,它与其他答案相同,但奇怪的是,到目前为止,没有其他人显示带有前导管道的代码。
    【解决方案4】:

    除了 DigitalRoss 的示例之外,如果您更喜欢 $() 而不是反引号 `,可以使用另一种形式

    echo abc $(: comment) \
         def $(: comment) \
         xyz
    

    当然,您也可以使用带有反引号的冒号语法:

    echo abc `: comment` \
         def `: comment` \
         xyz
    

    补充说明

    $(#comment) 不起作用的原因是,一旦它看到#,它将把该行的其余部分视为 cmets,包括右括号:comment)。所以括号永远不会关闭。

    反引号的解析方式不同,即使在 # 之后也会检测到结束反引号。

    【讨论】:

    • 会为每条评论创建一个新的外壳吗?
    【解决方案5】:

    这会产生一些开销,但从技术上讲,它确实回答了您的问题:

    echo abc `#Put your comment here` \
         def `#Another chance for a comment` \
         xyz, etc.
    

    特别是对于管道,有一个没有开销的干净解决方案:

    echo abc |        # Normal comment OK here
         tr a-z A-Z | # Another normal comment OK here
         sort |       # The pipelines are automatically continued
         uniq         # Final comment
    

    查看堆栈溢出问题How to Put Line Comment for a Multi-line Command

    【讨论】:

    • 看起来相当复杂,如果没有更简单的方法?
    • 好的,我添加了一个稍微简单的变体。
    • 您能否修改您的答案以显示不需要反斜杠的事实,以便我可以将 cmets 放在每行旁边并使用管道?
    • 我验证了版本一和二可以正常工作。但是,您能解释一下他们为什么这样做以及这里发生了什么吗?谢谢。
    • 感谢您的解释。我在 unix.sx 上提出了一个问题,询问更多详细信息,bash multi line command with comments after the continuation character
    【解决方案6】:

    $IFS评论黑客

    这个hack在$IFS上使用parameter expansion,用于分隔命令中的单词:

    $ echo foo${IFS}bar
    foo bar
    

    同样:

    $ echo foo${IFS#comment}bar
    foo bar
    

    使用它,您可以在带有延续的命令行上添加注释:

    $ echo foo${IFS# Comment here} \
    > bar
    foo bar
    

    但注释必须在\ 延续之前。

    注意参数扩展是在注释内部进行的:

    $ ls file
    ls: cannot access 'file': No such file or directory
    $ echo foo${IFS# This command will create file: $(touch file)}bar
    foo bar
    $ ls file
    file
    

    罕见的例外

    唯一失败的情况是$IFS 之前以 exact text 开头,该文本通过扩展删除(即,在 # 字符之后):

    $ IFS=x
    $ echo foo${IFS#y}bar
    foo bar
    $ echo foo${IFS#x}bar
    foobar
    

    注意最后的foobar 没有空格,说明问题。

    由于默认情况下$IFS 仅包含空格,因此您不太可能遇到此问题。


    感谢@pjh's comment 引发了这个答案。

    【讨论】:

      【解决方案7】:

      正如 DigitalRoss 所指出的,当行以| 结尾时,尾随反斜杠不是必需的。您可以将 cmets 放在 | 之后的一行:

       cat ${MYSQLDUMP} |         # Output MYSQLDUMP file
       sed '1d' |                 # skip the top line
       tr ",;" "\n" | 
       sed -e 's/[asbi]:[0-9]*[:]*//g' -e '/^[{}]/d' -e 's/""//g' -e '/^"{/d' |
       sed -n -e '/^"/p' -e '/^print_value$/,/^option_id$/p' |
       sed -e '/^option_id/d' -e '/^print_value/d' -e 's/^"\(.*\)"$/\1/' |
       tr "\n" "," |
       sed -e 's/,\([0-9]*-[0-9]*-[0-9]*\)/\n\1/g' -e 's/,$//' |   # hate phone numbers
       sed -e 's/^/"/g' -e 's/$/"/g' -e 's/,/","/g' >> ${CSV}
      

      【讨论】:

        【解决方案8】:

        结尾的反斜杠必须是该行的最后一个字符,才能将其解释为继续命令。后面不允许有 cmets 甚至空格。

        您应该能够在命令之间添加注释行

        # output MYSQLDUMP file
        cat ${MYSQLDUMP} | \
        # simplify the line
        sed '/created_at/d' | \
        # create some newlines
        tr ",;" "\n" | \
        # use some sed magic
        sed -e 's/[asbi]:[0-9]*[:]*//g' -e '/^[{}]/d' -e 's/""//g' -e '/^"{/d' | \
        # more magic
        sed -n -e '/^"/p' -e '/^print_value$/,/^option_id$/p' | \
        # even more magic
        sed -e '/^option_id/d' -e '/^print_value/d' -e 's/^"\(.*\)"$/\1/' | \
        tr "\n" "," | \
        # I hate phone numbers in my output
        sed -e 's/,\([0-9]*-[0-9]*-[0-9]*\)/\n\1/g' -e 's/,$//' | \ 
        # one more sed call and then send it to the CSV file
        sed -e 's/^/"/g' -e 's/$/"/g' -e 's/,/","/g' >> ${CSV}
        

        【讨论】:

        • 当管道命令组件以|结尾时,\不是必需的
        • DigitalRoss,你是对的,我可以只使用管道而不是反斜杠,然后我的#cmets 就可以正常工作了......你能把它作为答案发布,这样我就可以接受了。跨度>
        • "您应该能够在命令之间添加注释行":不,这只是因为前几行的最后一个解释字符是|。如果你尝试cat file1\&lt;newline&gt;#comment&lt;newline&gt;file2,你会发现你得到的不是cat file1 file2,而是cat file1; file2
        • 但是,正如其他人所提到的,cat file1 | # comment&lt;newline&gt;sort 工作正常。 cat file1 &amp;&amp; # comment&lt;newline&gt;echo foo 也是如此。所以 cmets 可以包含在 |&amp;&amp;|| 之后,但不能在 `\` 之后或命令中间。
        【解决方案9】:

        反斜杠将 # 转义,将其解释为文字字符而不是注释字符。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-30
          • 2017-05-02
          • 2020-01-02
          • 1970-01-01
          • 1970-01-01
          • 2016-07-25
          • 2012-10-16
          • 2021-05-20
          相关资源
          最近更新 更多