【问题标题】:Hidden features of BashBash 的隐藏功能
【发布时间】:2010-09-17 16:53:56
【问题描述】:

Shell 脚本通常用作粘合剂,用于自动化和简单的一次性任务。您最喜欢 Bash shell/脚本语言的哪些“隐藏”功能?

  • 每个答案一个功能
  • 提供功能示例和简短说明,而不仅仅是文档链接
  • 使用粗体标题作为第一行标记功能

另见:

【问题讨论】:

    标签: bash shell scripting hidden-features


    【解决方案1】:

    插入前一行的最后一个参数

    alt-. 有史以来最有用的组合键,试试看,不知为何没人知道这个组合键。

    一次又一次地按下它以选择较旧的最后一个参数。

    当您想对刚才使用的东西做其他事情时非常棒。

    【讨论】:

    • 绝对+1。感谢这个,非常有用但又如此隐蔽。
    • 我可以使用 Alt+.给你+2?
    • 我是 !$ 的常客,但这更直接更有用。
    • 我发现!$ 很难快速输入。我总是不得不放慢脚步,考虑把美元符号放在第二位。 Alt+. 更快更容易。更不用说,您实际上可以在执行之前看到文本。
    • 你也可以用alt+-[0-9]切换参数,然后alt+。
    【解决方案2】:

    如果您想在注销后保持进程运行:

    disown -h <pid>

    是一个有用的内置 bash。与nohup 不同,您可以在已经运行的进程上运行disown

    首先,使用 control-Z 停止工作,从 ps 获取 pid(或使用 echo $!),使用 bg 将其发送到后台,然后使用带有 -h 标志的 disown

    不要忘记后台你的工作,否则当你注销时它会被杀死。

    【讨论】:

    • 太甜了!很多次我都想这样做。之后您还可以重定向输出吗?
    • 最好从jobs -l(或-p)获取PID
    • Eh - 我曾经写过一个 C 程序,本质上是 fork()exec() 它的参数,本质上是一个守护进程。我可以轰炸外壳并一口气运行一些东西bgexec google-chrome && exit
    • 这不是和screen 的功能一样吗?
    【解决方案3】:

    手册中 EXPANSION 部分列出的几乎所有内容

    特别是参数扩展:

    $ I=foobar
    $ echo ${I/oo/aa} #replacement
    faabar
    $ echo ${I:1:2}   #substring
    oo
    $ echo ${I%bar}   #trailing substitution
    foo
    $ echo ${I#foo}   #leading substitution
    bar
    

    【讨论】:

    • 很好,这就是我在 cmd.exe 中获得 %Ix=y% ... :)
    【解决方案4】:

    我最喜欢的:

    sudo !!
    

    使用 sudo 重新运行上一个命令。

    【讨论】:

    【解决方案5】:

    更多魔法组合键:

    • Ctrl + r 在您的命令历史记录中开始“反向增量搜索”。当您继续键入时,它会检索包含您输入的所有文本的最新命令。

    • Tab 补全您目前输入的单词,如果它没有歧义的话。

    • Tab Tab 列出您目前输入的单词的所有补全。

    • Alt + * inserts 所有可能的补全,这特别有用,例如,如果您刚刚输入了具有潜在破坏性的命令通配符:

      rm -r source/d*.c Alt + *
      rm -r source/delete_me.c source/do_not_delete_me.c

    • Ctrl + Alt + e 对当前行执行别名、历史记录和 shell 扩展。换句话说,当前行被重新显示,因为它将被 shell 处理:

      ls $HOME/tmp Ctrl Alt + e
      ls -N --color=tty -T 0 /home/cramey

    【讨论】:

    • +1 表示 alt+*(或 alt-shift-8),让您看到即将犯的错误
    • 我打开了 show-all-if-ambiguous 以防止两次点击 Tab。
    【解决方案6】:

    取回历史命令和参数

    可以使用! 运算符选择性地访问以前的命令和参数。当您处理长路径时,它非常有用。

    您可以使用history 检查您的最后命令。

    您可以使用以前的命令,!<n>n 中的命令在 history 中的索引,负数从历史上的最后一个命令开始倒数。

    ls -l foo bar
    touch foo bar
    !-2
    

    !:<n> 可以使用之前的参数,0 是命令,>= 1 是参数。

    ls -l foo
    touch !:2
    cp !:1 bar
    

    您可以将两者与!<n>:<m>结合使用

    touch foo bar
    ls -l !:1 !:2
    rm !-2:1 !-2:2
    !-2
    

    您还可以使用参数范围!<n>:<x>-<y>

    touch boo far
    ls -l !:1-2
    

    其他! 特殊修饰符有:

    • * 用于所有参数

      ls -l foo bar
      ls !*
      
    • ^ 用于第一个参数 (!:1 == !^)

    • $ 最后一个参数

      ls -l foo bar
      cat !$ > /dev/null
      

    【讨论】:

    • ^R 键盘快捷键也很方便
    • 我也喜欢 alt-^(美国键盘上的 alt-shift-6)。它扩展了 !:2 之类的历史序列,因此您可以在运行命令之前查看命令将要执行的操作。
    • 而不是$!尝试输入 ESC+。最后一个参数将出现在您的光标下方。
    【解决方案7】:

    我喜欢 -x 功能,它允许查看脚本中发生了什么。

    bash -x script.sh 
    

    【讨论】:

      【解决方案8】:

      SECONDS=0; sleep 5 ; echo "that took approximately $SECONDS seconds"

      每次这个参数 引用,秒数 因为返回了 shell 调用。 如果给 SECONDS 赋值, 随后返回的值 参考是秒数 因为赋值加上值 分配的。如果 SECONDS 未设置,则 失去了它的特殊属性,即使 它随后被重置。

      【讨论】:

        【解决方案9】:

        这是我的最爱之一。这将选项卡完成设置为不区分大小写。它非常适合快速输入目录路径,尤其是在文件系统默认不区分大小写的 Mac 上。我把它放在我的主文件夹中的.inputrc 中。

        set completion-ignore-case on
        

        【讨论】:

        • 我不知道这个,谢谢。这在我的 ~/.inputrc 中已经被注释掉了。我打开了它并显示所有如果不明确。
        【解决方案10】:

        特殊变量random:

        if [[ $(($RANDOM % 6)) = 0 ]]
            then echo "BANG"
        else
            echo "Try again"
        fi   
        

        【讨论】:

        • # [ $[ $RANDOM % 6 ] == 0 ] && rm -rf / ||回声“你活着”:)
        • 算术评估中不需要$ sigil;无需单独评估和测试:if (( RANDOM % 6 == 0 )); then echo "BANG"; else echo "Try again"; fi 甚至更短:(( RANDOM % 6 )) && echo "Try again" || echo "BANG"
        【解决方案11】:

        正则表达式处理

        最近 bash 发布的功能正则表达式匹配,所以你可以这样做:

        if [[ "mystring" =~ REGEX ]] ; then  
            echo match
        fi
        

        其中 REGEX 是格式由 man re_format 描述的原始正则表达式。

        任何括号部分的匹配都存储在 BASH_REMATCH 数组中,从元素 1 开始(元素 0 是整个匹配的字符串),因此您也可以使用它来进行正则表达式驱动的解析。

        【讨论】:

        • 不用将正则表达式括在引号中感觉有点奇怪。 . .
        【解决方案12】:

        Ctrlx Ctrle

        这会将当前命令加载到变量 VISUAL 中定义的编辑器中。这对于此处列出的一些长命令非常有用。

        使用 vi 作为编辑器:

        export VISUAL=vi
        

        【讨论】:

        • set -o vi 然后 Esc 命令进入内联编辑,简单的“v”将命令拉入完整的 vi 编辑器。
        【解决方案13】:

        快速和肮脏的错别字更正(特别适用于慢速连接上的长命令,因为使用命令历史并滚动浏览它会很糟糕):

        $ cat /proc/cupinfo
        cat: /proc/cupinfo: No such file or directory
        $ ^cup^cpu
        

        还可以尝试!:s/old/new,它在上一个命令中将旧的替换为新的一次。

        如果您想替换多次出现,您可以使用!:gs/old/new 进行全局替换。

        您可以将gss 命令用于任何历史事件,例如

        !-2:s/old/new
        

        在倒数第二个命令中将old 替换为new(一次)。

        【讨论】:

        • 有没有办法找到更多关于这个或类似功能的信息?用谷歌搜索 ^foo^bar 并不是那么令人满意:)
        • man bash 中的事件指示符和修饰符。尝试!:s/old/new 将旧命令替换为新命令一次。如果要替换多次出现,可以使用!:gs/old/new 进行全局替换。这可以与 James 的帖子 (stackoverflow.com/questions/211378/hidden-features-of-bash/…) 结合使用,例如:!$:s/old/new(在上一个命令的最后一个参数中用 new 代替 old),!-2:0:gs/a/s !-2*(在倒数第二个命令名称中用 s 代替每次出现的 a并添加倒数第二个命令的所有参数)。祝你好运!
        【解决方案14】:

        这是我最喜欢的两个:

        要检查没有真正执行脚本的语法,请使用:

        bash -n script.sh
        

        返回上一个目录(是的,我知道 pushd 和 popd,但这样更快)

        cd -
        

        【讨论】:

        • "cd -" 如果您忘记将目录推送到堆栈但仍想返回那里,则具有工作的优势。
        【解决方案15】:

        使用中缀布尔运算符

        考虑简单的 if:

        if [ 2 -lt 3 ]
            then echo "Numbers are still good!"
        fi
        

        那个 -lt 看起来有点丑。不是很现代。如果您在布尔表达式周围使用双括号,则可以使用普通的布尔运算符!

        if [[ 2 < 3 ]]
            then echo "Numbers are still good!"
        fi
        

        【讨论】:

        • 这不是 Bash 的一个特性,而是一个外部程序:是的,'[[' 是一个独立的程序。
        • madmath:我想你会发现 [ 通常是要测试的符号链接或硬链接,而 [[ 是内置的 shell。它需要由 shell 解析,否则
        • 不,'[' 是一个独立的程序。 '[[' 不是
        • $ type [[ [[ 是一个shell关键字 $ which [[ $ # 无输出
        • 显然 SO 不喜欢 cmets 中的换行符,希望这不会太难解析。那是在带有 Bash 3.2.39 的 Ubuntu 上,顺便说一句。
        【解决方案16】:

        数组:

        #!/bin/bash
        
        array[0]="a string"
        array[1]="a string with spaces and \"quotation\" marks in it"
        array[2]="a string with spaces, \"quotation marks\" and (parenthesis) in it"
        
        echo "There are ${#array[*]} elements in the array."
        for n in "${array[@]}"; do
            echo "element = >>${n}<<"
        done
        

        可以在Advanced Bash-Scripting Guide 中找到有关数组(和其他高级 bash 脚本内容)的更多详细信息。

        【讨论】:

          【解决方案17】:

          在显示 bash 提示符之前运行命令

          在“PROMPT_COMMAND”环境变量中设置一个命令,它将在每个提示之前自动运行。 示例:

          [lsc@home]$ export PROMPT_COMMAND="date"
          Fri Jun  5 15:19:18 BST 2009
          [lsc@home]$ ls
          file_a  file_b  file_c
          Fri Jun  5 15:19:19 BST 2009
          [lsc@home]$ ls
          

          对于下一个愚人节,将“export PROMPT_COMMAND=cd”添加到某人的 .bashrc,然后坐下来观察混乱的发展。

          【讨论】:

            【解决方案18】:

            来自 bash man 页面的魔法组合键:

            • Ctrl + aCtrl + e 将光标移动到分别是当前行。

            • Ctrl + tAlt + t 用当前的,然后将光标向前移动。

            • Alt + uAlt + l 转换当前单词(从光标到结尾)大写和小写。

              提示:Alt + 后跟这些命令中的任何一个来转换当前的 开始单词。


            奖金man 提示:

            • 查看man 页面时,使用/ 在页面中搜索文本。使用 n 跳转到下一场比赛或使用 N 跳转到上一场比赛。

            • 利用其格式,加快您在 man 页面中搜索特定命令或子部分的速度:

              o 不要键入 /history expansion 来查找该部分,而是尝试 /^history,使用插入符号 (^) 仅查找 以“历史”开头

              o 尝试使用 /   read 并在前面加上几个空格,以搜索该内置命令。内置函数始终在 man 页面中缩进。

            【讨论】:

              【解决方案19】:

              export TMOUT=$((15*60))

              在 15 分钟空闲时间后终止 bash,设置为 0 以禁用。我通常把它放在我的根帐户上的 ~/.bashrc 中。它在管理您的盒子时很方便,您可能会在离开终端之前忘记注销。

              【讨论】:

                【解决方案20】:

                撤消

                C-S-- Control Shift Minus 撤消键入操作。

                杀死/猛击

                任何删除操作Cw(删除前一个单词),Ck(删除到行尾),Cu(删除到行首) 等... 将已删除的文本复制到终止环,您可以使用以下命令粘贴最后一个终止:Cy 并使用 Alt-y 循环(并粘贴)已删除项目的环

                【讨论】:

                  【解决方案21】:

                  通过设置FIGNORE 变量,您可以在制表符完成时忽略某些文件。

                  例如,如果您有一个 subverion 存储库,并且想要更轻松地导航,则可以这样做

                  export FIGNORE=".svn"
                  

                  现在您可以cd 而不会被.svn 目录阻止。

                  【讨论】:

                    【解决方案22】:

                    使用算术:

                    if [[ $((2+1)) = $((1+2)) ]]
                        then echo "still ok"
                    fi
                    

                    【讨论】:

                    • 令人惊讶的是有多少人不知道这一点,并在他们的脚本中使用 expr。
                    • 有时算术展开就足够了:((2 + 1 == 1 + 2))&&echo OK
                    【解决方案23】:

                    大括号扩展

                    使用 {x,y,z} 进行标准扩展:

                    $ echo foo{bar,baz,blam}
                    foobar foobaz fooblam
                    $ cp program.py{,.bak}  # very useful with cp and mv
                    

                    用 {x..y} 进行序列扩展:

                    $ echo {a..z}
                    a b c d e f g h i j k l m n o p q r s t u v w x y z
                    $ echo {a..f}{0..3}
                    a0 a1 a2 a3 b0 b1 b2 b3 c0 c1 c2 c3 d0 d1 d2 d3 e0 e1 e2 e3 f0 f1 f2 f3
                    

                    【讨论】:

                      【解决方案24】:

                      我最近阅读了Csh Programming Considered Harmful,其中包含这个惊人的宝石:

                      考虑管道:

                      A | B | C
                      

                      你想知道 C 的状态,嗯,很简单:它在 $? 中,或者 csh 中的 $ 状态。但是如果你想从 A 那里得到它,那你就不走运了——如果 你在 csh 中,就是这样。在 Bourne shell 中,你可以得到它,虽然 这样做有点棘手。 这是我在运行 dd 时必须做的事情 stderr 进入 grep -v 管道以消除记录输入/输出噪音,但有 返回 dd 的退出状态,而不是 grep 的:

                      device=/dev/rmt8
                      dd_noise='^[0-9]+\+[0-9]+ records (in|out)$'
                      exec 3>&1
                      status=`((dd if=$device ibs=64k 2>&1 1>&3 3>&- 4>&-; echo $? >&4) |
                          egrep -v "$dd_noise" 1>&2 3>&- 4>&-) 4>&1`
                      exit $status;
                      

                      【讨论】:

                      • 你想要的是使用 PIPESTATUS 变量,它是管道中每个命令的退出状态的数组。 ${PIPESTATUS[0]} 就是你想要的。
                      • 史蒂夫,我从来不知道 - 在这里发布它作为答案! (如果你愿意,我会支持它:)
                      【解决方案25】:

                      截断文件的内容(归零文件)

                      > file
                      

                      具体来说,这对于截断日志文件非常有用,当文件被另一个进程打开时,仍然可能写入文件。

                      【讨论】:

                      • 为了防止您意外截断文件,set -o noclobber。然后你需要使用&gt;| file 来截断一个文件。
                      【解决方案26】:

                      不是一个真正的功能,而是一个方向:我在commandlinefu.com 发现了许多“隐藏功能”、秘密和各种 bash 实用性。这个答案的许多评价最高的答案,我在那个网站上学到的:)

                      【讨论】:

                        【解决方案27】:

                        另一个小的: Alt+#

                        退出当前行并将其移入历史缓冲区。

                        因此,当您组装命令行时,您需要发出临时命令,例如找到一个文件,你只需按 alt+#,发出另一个命令,进入历史记录,取消注释并继续。

                        【讨论】:

                          【解决方案28】:

                          用大括号代替 for 循环中的 dodone

                          For 循环体通常在do...done(只是一个例子):

                          for f in *;
                          do
                              ls "$f";
                          done
                          

                          但我们可以使用大括号来使用 C 风格:

                          for f in *; {
                              ls "$f";
                          }
                          

                          我认为这看起来比do...done更好,我更喜欢这个。我还没有在任何 Bash 文档中找到它,所以这确实是一个隐藏功能。

                          【讨论】:

                            【解决方案29】:

                            C 风格的数字表达式:

                            let x="RANDOM%2**8"
                            echo -n "$x = 0b"
                            for ((i=8; i>=0; i--)); do
                              let n="2**i"
                              if (( (x&n) == n )); then echo -n "1"
                              else echo -n "0"
                              fi
                            done
                            echo ""
                            

                            【讨论】:

                              【解决方案30】:

                              这些属性是我最喜欢的另一个。

                              export HISTCONTROL=erasedups
                              export HISTSIZE=1000
                              

                              第一个确保 bash 不会多次记录命令,这将真正提高 history 的实用性。另一个将历史记录大小从默认的 100 扩展到 1000。我实际上在我的机器上将它设置为 10000。

                              【讨论】:

                                猜你喜欢
                                • 2020-08-14
                                • 2010-09-13
                                • 2010-09-23
                                • 2020-06-01
                                • 2011-07-21
                                • 1970-01-01
                                • 2012-09-23
                                • 2010-09-08
                                • 2011-02-03
                                相关资源
                                最近更新 更多