Bash的这条神奇路线我无法理解,有人能解释一下它是什么吗';他在干什么?

Bash的这条神奇路线我无法理解,有人能解释一下它是什么吗';他在干什么?,bash,sed,grep,Bash,Sed,Grep,我在这条线上找到了它: 我试图在代码中使用它: 这是我不明白的部分,特别是第三行 [ $# = 0 ] && help while [ $# -gt 0 ]; do CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" go.sh | sed -e "s/-/_/g") if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not suppor

我在这条线上找到了它:

我试图在代码中使用它:

这是我不明白的部分,特别是第三行

[ $# = 0 ] && help
while [ $# -gt 0 ]; do
  CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" go.sh | sed -e "s/-/_/g")
  if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not supported"; exit 1; fi
  shift; eval "$CMD" $@ || shift $? 2> /dev/null
谢谢你的帮助

bash(1)
并不是那么“神奇”,尽管它确实让用户感觉自己像个巫师!破译它的关键是知道要查看哪些手册页。或者,如果你是一个语言人(像我一样),认识到bash是一种小小的粘合语言;其他一些小语言以小工具的形式散布在它上面,比如
grep(1)
sed(1)
awk(1)
cut(1)
,等等

那么,让我们深入探讨一下:

[ $# = 0 ] && help
这就是说,运行
[
测试命令;如果成功,则运行help命令。
$#
是参数数。在shell上运行
帮助测试
,以了解其工作原理(
帮助
仅适用于需要的其他内置代码
man(1)

while循环的开始。注意:大多数现代bash程序员会使用
[[$\35;>0]]
(除非他们需要兼容POSIX sh)或
(($\35;>0))

在parens中运行这些东西,并将输出存储在CMD中(bash的手册阐明了这是如何工作的)

go.sh
中搜索特定模式(有关正则表达式的详细信息,请参见
man re_format
,有关选项的作用,请参见
man grep
。例如,
-o
表示只打印匹配项,而不是整行)

将grep的结果作为sed的输入输出。
mansed
对于它的小语言来说是一个很好的资源。
s/-/\ug
将所有
-
替换为
;更惯用的方法是使用tr而不是sed:
tr-

if [ -z ... ]
测试参数是否为空。if的其余部分记录消息并退出

shift
$@
中弹出一个命令行参数,因此现在少了一个

eval "$CMD" "$@"
使用剩余的参数运行CMD字符串(最好在这里使用数组)

|| shift $? 2>/dev/null
如果失败,则将尽可能多的参数转换为退出代码(
$?
),并将错误消息重定向到
/dev/null
(例如,当没有足够的参数进行转换时)

这一部分有点奇怪,可能只有在应用程序的上下文中才有意义。通常,退出代码不会告诉您要转换什么。但是您可以这样编程

CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" go.sh | sed -e "s/-/_/g")
上面的行是从文件
go.sh
中最大映射1行(-m1),仅匹配正则表达式(
-Po
):
“^##*$1,--\K[^=]*.-\K${1#--}(?:[=])”
$1
被while循环中当前正在处理的脚本参数替换)
然后grep命令的结果通过管道传输到
sed
,其中每个'-'都被更改为'.':
sed-e“s/-/\ug”
。这个操作的结果字符串成为CMD变量的值。

另一个答案解释了脚本的其余部分,我将尝试解释grep

  • grep
    • -m1
      -仅打印第一个匹配项
    • -P
      -使用perl正则表达式
    • -o
      -仅打印匹配的字符串
    • “^##*$1,--\K[^=]*.-\K${1}(?:[=])”
      • |
        或两个正则表达式。因此,我们搜索一个正则表达式或另一个正则表达式
      • \K
        “重置行位置”。基本上这意味着使用
        -o
        它将把
        \K
        的结果打印到与正则表达式匹配的末尾。它通常一起使用
        grep-Po'blablablabla\Kblabla'。例如
        echo abcde | grep-P'ab\K..
        将打印
        de`
    • ^##*$1,--\K[^=]*
      • 搜索一行
        ##,-->
        。然后打印出零件
        。因此,从
        -->
        到下一个空格或
        =
        打印出来
    • ^##.*-\K${1#--}(?:[=])
      • (?:…)
        是perl中“不使用内存”的分组。
        (?:某物)
        (某物)
        相同
      • 搜索转到
        ##--
    • 因此,基本上grep希望找到类似
      ##first_arg,-
      ##blabla--
      的内容,其中
      first_arg
      是脚本中当前的第一个参数,它将输出
      中的部分
  • sed
    • “s/-/\ug/g”
      -只需将所有
      -
      替换为

真正的魔力在于参数扩展和使用
grep-P
\K
以及lookaheads。天哪,你太棒了!多亏了你的智慧,我现在正在重写整个部分,它将变得干净易读!@moop谢谢!我做了很多bash编程;有(与某些信念相反)最佳实践(特别是可读性和错误处理)。我的一些脚本在GitHub上,应该链接到我的个人资料中。另外,请参阅,这是,我的脚本达到了任何质量标准吗?小心ABS,它是一个混合包。你发现什么让人困惑?它给CMD赋值。它给的值是一个写得不好的grep的输出,它通过管道传输到sed,应该使用
tr
,这应该告诉您应该避免使用代码,而不是模拟代码。这里唯一可能让人困惑的是regex,它绝对应该简化。也许
${1#--}
也让您困惑。我不会模拟此代码。避免使用它。
eval“$CMD”$@| | shift$?
应该会让你尖叫着跑。@williampersell我觉得很困惑。这解决了正则表达式问题!我从调试器及其
shift
eval "$CMD" "$@"
|| shift $? 2>/dev/null
CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" go.sh | sed -e "s/-/_/g")