noop [:] 在 bash 中的用例是什么



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

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

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

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

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

while true; do
  echo 'Going on forever'
done

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

while keep_waiting; do
  : # busy-wait
done

内置:可以追溯到 Thompson shell,它存在于 Unix v6 中。 :是汤普森壳goto声明的标签指标。标签可以是任何文本,因此:兼作注释指示器(如果没有goto comment,则: comment实际上是注释(。伯恩的外壳没有goto,但保留了:

使用:的常见成语是 : ${var=VALUE} ,如果未设置,则var设置为VALUE,如果已经设置var则不执行任何操作。此构造仅以变量替换的形式存在,并且此变量替换需要以某种方式成为命令的一部分:no-op 命令可以很好地发挥作用。

参见 冒号内置有什么用途?。

当我注释掉所有代码时,我将它用于 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

如果使用 set -e ,那么|| :是在发生故障时退出脚本的好方法(它显式使其通过(。

忽略

alias参数

有时,您希望拥有一个不需要任何参数的别名。您可以使用:来做到这一点:

> alias alert_with_args='echo hello there'
> alias alert='echo hello there;:'
> alert_with_args blabla
hello there blabla
> alert blabla
hello there

您可以使用:来提供成功但不执行任何操作的命令。 在此示例中,默认情况下关闭"详细"命令,方法是将其设置为 : 。"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  

一种用途是作为多行注释,或者通过将部分代码与 here 文件结合使用来注释掉部分代码以进行测试。

: << 'EOF'
This part of the script is a commented out
EOF

不要忘记在EOF周围使用引号,这样就不会评估其中的任何代码,例如$(foo)。使用直观的终止符名称(如 NOTESSCRATCHPADTODO (也可能值得。

我的两个。

嵌入 POD 注释

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

运行时函数绑定

这是一种用于在运行时绑定函数的代码模式。F.i.,有一个调试函数,只有在设置了某个标志时才做某事:

#!/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

与这个答案有点相关,我发现这种无操作对于破解多语言脚本相当方便。例如,这里有一个针对 bash 和 vimscript 的有效注释:

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

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


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

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

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

    #!/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文件。

假设你有一个命令,你希望链接到另一个命令的成功:

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 || :

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


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

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

这可能是一个意见问题,但这里有一个例子。 假设您创建了一个通过采用两条 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

一些开发人员会想要删除无操作,但这意味着否定条件:

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

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

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

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

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

null 命令 [:] 实际上被认为是内置 true 的 shell 的同义词。":" 命令本身是内置的 Bash,其退出状态为 true (0(。`$ :$ 回声 $? # 0

while :
do
   operation-1
   operation-2
   ...
   operation-n
done
# Same as:
    while true
    do
      ...
    done

如果/然后测试中的占位符:

if condition
then :   # Do nothing and branch ahead
else     # Or else ...
   take-some-action
fi

$ : ${username=`whoami`}
$ ${username=`whoami`}   #Gives an error without the leading :

来源:TLDP

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

RUN : 
    && somecommand1 
    && somecommand2 
    && somecommand3

对我来说,它读起来比:

RUN somecommand1 
    && somecommand2 
    && somecommand3

但这当然只是偏好问题

当我必须创建一个模拟睡眠函数以用于蝙蝠测试框架时,我今天使用了 noop。这允许我创建一个没有副作用的空函数:

function sleep() {
  :
}

相关内容

  • 没有找到相关文章

最新更新