155

我在 bash (:) 中搜索了 noop,但找不到任何好的信息。该运算符的确切目的或用例是什么?

我尝试了以下操作,它对我来说是这样的:

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

请让我知道,该运算符的任何实时用例或强制使用它的任何地方。

4

12 回答 12

219

更多是出于历史原因。内置冒号:完全等同于true. 当返回值很重要时使用它是传统true的,例如在无限循环中:

while true; do
  echo 'Going on forever'
done

:当 shell 语法需要命令但您无事可做时使用它是传统的。

while keep_waiting; do
  : # busy-wait
done

内置的:日期可以追溯到Thompson shell,它出现Unix v6中。:是 Thompson shellgoto语句的标签指示符。标签可以是任何文本,因此可以:兼作评论指示符(如果没有goto comment,则: comment实际上是评论)。Bourne shell没有但goto保留:.

一个常见的习惯用法:: ${var=VALUE},如果未设置则设置var为 ,VALUE如果已设置则不执行任何操作var。这种结构仅以变量替换的形式存在,并且这种变量替换需要以某种方式成为命令的一部分:无操作命令很好地服务。

另请参阅内置冒号的用途是什么?.

于 2012-09-13T11:51:13.273 回答
33

当我注释掉所有代码时,我将它用于 if 语句。例如你有一个测试:

if [ "$foo" != "1" ]
then
    echo Success
fi

但是您想暂时注释掉其中包含的所有内容:

if [ "$foo" != "1" ]
then
    #echo Success
fi

这会导致 bash 给出语法错误:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash 不能有空块(WTF)。所以你添加一个无操作:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

或者您可以使用 no-op 注释掉以下行:

if [ "$foo" != "1" ]
then
    : echo Success
fi
于 2017-02-15T11:09:40.900 回答
15

如果您使用set- ethen|| :是一种在发生故障时退出脚本的好方法(它显式使其通过)。

于 2016-07-12T15:04:31.633 回答
7

您将使用:提供一个成功但不执行任何操作的命令。在本例中,“详细”命令默认关闭,将其设置为:. 'v' 选项将其打开。

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  
于 2016-05-11T18:33:57.943 回答
6

忽略alias参数

有时您希望有一个不带任何参数的别名。您可以使用:

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there
于 2017-07-05T21:26:53.437 回答
6

一种用途是作为多行注释,或将部分代码与此处的文件结合使用以进行测试以注释掉部分代码。

: << 'EOF'

This part of the script is a commented out

EOF

不要忘记使用引号,EOF这样里面的任何代码都不会被评估,比如$(foo). 也可能值得使用直观的终止符名称,如NOTES,SCRATCHPADTODO

于 2017-11-11T16:58:18.403 回答
3

我的两个。

嵌入 POD 评论

一个非常时髦的应用:在 bash 脚本中嵌入 POD 注释,这样可以快速生成手册页。当然,最终会用 Perl 重写整个脚本 ;-)

运行时函数绑定

这是一种在运行时绑定函数的代码模式。Fi,只有在设置了某个标志时才具有调试功能:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

你得到:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar
于 2016-10-28T19:50:32.157 回答
2

有时无操作子句可以使您的代码更具可读性。

这可能是一个意见问题,但这里有一个例子。假设您创建了一个通过两条 unix 路径工作的函数。它计算从一条路径到另一条路径所需的“更改路径”。您对函数进行了限制,即路径都必须以“/”开头,或者两者都不能。

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

一些开发人员会想要删除 no-op 但这意味着否定条件:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

现在 - 在我看来 - 从 if 子句中并不清楚您想要跳过执行该功能的条件。为了消除无操作并清楚地做到这一点,您需要将 if 子句移出函数:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

这看起来更好,但很多时候我们不能这样做;我们希望在函数内部完成检查。

那么这种情况多久发生一次呢?不经常。也许一年一次或两次。它经常发生,你应该意识到它。当我认为它提高了我的代码的可读性(不管是什么语言)时,我不会回避使用它。

于 2014-11-05T14:18:52.450 回答
2

假设您有一个命令要链接到另一个命令的成功:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

但是现在您想有条件地执行命令,并且您想显示将要执行的命令(空运行):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

所以如果你设置了 DEBUG 和 NOEXEC,第二个命令永远不会出现。这是因为第一个命令从不执行(因为 NOEXEC 不为空),但对该事实的评估使您返回 1,这意味着从属命令从不执行(但您希望它执行,因为它是空运行)。所以要解决这个问题,您可以使用 noop 重置堆栈中留下的退出值:

[ -z "$NOEXEC" ] && $cmd || :
于 2019-06-21T23:11:58.860 回答
2

与这个答案有点相关,我发现这个无操作很方便破解多语言脚本。例如,下面是 bash 和 vimscript 的有效注释:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

当然,我们可能也使用true过,但是:作为一个标点符号而不是一个不相关的英语单词,它清楚地表明它是一个语法标记。


至于为什么有人会写一个多语言脚本这样棘手的事情(除了它很酷):它证明在我们通常会用几种不同的语言编写多个脚本文件的情况下很有帮助,其中 fileX指的是 file Y

在这种情况下,将两个脚本组合在一个单一的多语言文件中可以避免任何X确定路径的工作Y(它只是"$0")。更重要的是,它使移动或分发程序更加方便。

  • 一个常见的例子。shebangs 有一个众所周知的长期存在的问题:大多数系统(包括 Linux 和 Cygwin)只允许将一个参数传递给解释器。以下shebang:

    #!/usr/bin/env interpreter --load-libA --load-libB
    

    将触发以下命令:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"
    

    而不是预期的:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"
    

    因此,您最终会编写一个包装脚本,例如:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"
    

    这就是多语种进入舞台的地方。

  • 一个更具体的例子。我曾经写过一个 bash 脚本,其中包括调用 Vim。我需要给 Vim 额外的设置,这可以通过选项--cmd "arbitrary vimscript command here". 但是,该设置非常重要,因此将其内联在字符串中会很糟糕(如果可能的话)。因此,更好的解决方案是将其扩展写入某个配置文件中,然后让 Vim 使用-S "/path/to/file". 因此,我最终得到了一个多语言 bash/vimscript 文件。

于 2018-08-06T16:05:03.230 回答
1

我还在它的脚本中使用了定义默认变量。


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}

于 2020-07-01T00:05:41.573 回答
0

我有时在 Docker 文件上使用它来保持 RUN 命令对齐,如下所示:

RUN : \
    && somecommand1 \
    && somecommand2 \
    && somecommand3

对我来说,它读起来比:

RUN somecommand1 \
    && somecommand2 \
    && somecommand3

但这只是一个偏好问题,当然

于 2020-09-18T09:25:32.990 回答