一个什么都不做的命令的目的是什么,仅仅是一个评论领导者,但实际上是一个内置的shell?
这比在每个调用中将注释插入脚本大约40%要慢,这可能会根据注释的大小而有很大差异。我能看到的唯一可能原因是:
# poor man's delay function
for ((x=0;x<100000;++x)) ; do : ; done
# inserting comments into string of commands
command ; command ; : we need a comment in here for some reason ; command
# an alias for `true' (lazy programming)
while : ; do command ; done
我想我真正想要的是它可能具有的历史应用。
答案 0 :(得分:362)
历史,Bourne shell没有true
和false
作为内置命令。 true
只是简称为:
,false
为let 0
。
:
稍微好于true
,可以移植到古代Bourne派生的shell。举个简单的例子,考虑既没有!
管道运算符也没有||
列表运算符(就像一些古老的Bourne shell一样)。这使得else
语句的if
子句成为基于退出状态进行分支的唯一方法:
if command; then :; else ...; fi
由于if
需要非空then
子句且注释不计为非空,:
将作为无操作。
现在(即:在现代环境中)您通常可以使用:
或true
。两者都由POSIX指定,有些发现true
更容易阅读。但是有一个有趣的区别::
是所谓的POSIX 特殊内置,而true
是常规内置。
需要在shell中内置特殊的内置函数;常规内置插件仅“内置”,但并未严格保证。在大多数系统的PATH中,通常不应该有一个名为:
的常规程序,其功能为true
。
可能最重要的区别在于,使用特殊的内置函数,内置的任何变量 - 即使在简单命令评估期间的环境中 - 在命令完成后仍然存在,如此处使用ksh93所示: / p>
$ unset x; ( x=hi :; echo "$x" )
hi
$ ( x=hi true; echo "$x" )
$
请注意,Zsh忽略了这个要求,GNU Bash也是如此,除非在POSIX兼容模式下运行,但所有其他主要的“POSIX sh派生”shell都会观察到这一点,包括dash,ksh93和mksh。
另一个不同之处是常规内置插件必须与exec
兼容 - 这里使用Bash进行演示:
$ ( exec : )
-bash: exec: :: not found
$ ( exec true )
$
POSIX还明确指出:
可能比true
更快,但这当然是特定于实现的细节。
答案 1 :(得分:57)
我用它来轻松启用/禁用变量命令:
#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
vecho=":" # no "verbose echo"
else
vecho=echo # enable "verbose echo"
fi
$vecho "Verbose echo is ON"
因此
$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON
这样可以创建一个干净的脚本。这不能用'#'来完成。
此外,
: >afile
是保证'afile'存在但长度为0的最简单方法之一。
答案 2 :(得分:50)
一个有用的应用程序:如果你只对参数扩展的副作用感兴趣,而不是实际将结果传递给命令。在这种情况下,您使用PE作为参数:或者取决于您是否希望退出状态为0或1.示例可能是: "${var:=$1}"
。由于:
是内置的,因此应该非常快。
答案 3 :(得分:39)
:
也可以用于块注释(类似于C语言中的/ * * /)。例如,如果要跳过脚本中的代码块,可以执行以下操作:
: << 'SKIP'
your code block here
SKIP
答案 4 :(得分:30)
如果您要将文件截断为零字节,对清除日志很有用,请尝试以下操作:
:> file.log
答案 5 :(得分:27)
它类似于Python中的pass
。
一种用法是将函数存根直到它被写入:
future_function () { :; }
答案 6 :(得分:25)
其他答案中未提及的另外两种用途:
以此示例脚本:
set -x
: Logging message here
example_command
第一行set -x
使shell在运行之前打印出命令。这是一个非常有用的结构。缺点是通常echo Log message
类型的语句现在打印消息两次。冒号方法绕过那个。请注意,您仍然必须像echo
一样转义特殊字符。
我已经看到它被用于cron工作,如下:
45 10 * * * : Backup for database ; /opt/backup.sh
这是一个cron作业,每天10:45运行脚本/opt/backup.sh
。这种技术的优点在于,当/opt/backup.sh
打印一些输出时,它可以更好地查看电子邮件主题。
答案 7 :(得分:21)
您可以将它与反引号(``
)结合使用来执行命令而不显示其输出,如下所示:
: `some_command`
当然你可以some_command > /dev/null
,但:
- 版本稍微短一些。
话虽如此,我不建议实际这样做,因为它会让人感到困惑。它只是作为一个可能的用例而浮现在脑海中。
答案 8 :(得分:13)
它对多语言程序也很有用:
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }
现在这是一个可执行的shell脚本和一个JavaScript程序:意味着./filename.js
,sh filename.js
和node filename.js
都可以工作。
(绝对有点奇怪的用法,但仍然有效。)
根据要求进行一些解释:
逐行评估Shell脚本;运行时exec
命令终止shell,用结果命令替换它的进程。这意味着对于shell,程序看起来像这样:
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
只要单词中没有出现参数扩展或别名,shell脚本中的任何单词都可以用引号括起来而不改变其含义;这意味着':'
等同于:
(我们只在引号中包含它以实现下面描述的JavaScript语义)
...如上所述,第一行的第一个命令是无操作(它转换为: //
,或者如果您想引用这些词,':' '//'
请注意//
在这里没有任何特殊含义,就像它在JavaScript中一样;它只是一个被丢弃的无意义的词。)
最后,第一行(分号后面)的第二个命令是程序的真正含义:它是exec
调用,它取代了被调用的 shell脚本,调用Node.js进程来评估脚本的其余。
同时,JavaScript中的第一行解析为字符串文字(':'
),然后删除注释,删除;因此,对于JavaScript,程序看起来像这样:
':'
~function(){ ... }
由于string-literal本身就是一行,所以它是一个无操作语句,因此从程序中删除;这意味着删除整行,只留下 您的程序代码(在本例中为function(){ ... }
正文。)
答案 9 :(得分:11)
您还可以使用:
在文档中嵌入文档。
假设您有一个库脚本mylib.sh
,提供各种功能。您可以获取库(. mylib.sh
)并在此之后直接调用函数(lib_function1 arg1 arg2
),或者避免使命名空间混乱并使用函数参数(mylib.sh lib_function1 arg1 arg2
)调用库。 / p>
如果您还可以输入mylib.sh --help
并获取可用功能及其用法列表,那么这不是很好吗,而无需在帮助文本中手动维护功能列表吗?
#!/bin/bash # all "public" functions must start with this prefix LIB_PREFIX='lib_' # "public" library functions lib_function1() { : This function does something complicated with two arguments. : : Parameters: : ' arg1 - first argument ($1)' : ' arg2 - second argument' : : Result: : " it's complicated" # actual function code starts here } lib_function2() { : Function documentation # function code here } # help function --help() { echo MyLib v0.0.1 echo echo Usage: mylib.sh [function_name [args]] echo echo Available functions: declare -f | sed -n -e '/^'$LIB_PREFIX'/,/^}$/{/\(^'$LIB_PREFIX'\)\|\(^[ \t]*:\)/{ s/^\('$LIB_PREFIX'.*\) ()/\n=== \1 ===/;s/^[ \t]*: \?['\''"]\?/ /;s/['\''"]\?;\?$//;p}}' } # main code if [ "${BASH_SOURCE[0]}" = "${0}" ]; then # the script was executed instead of sourced # invoke requested function or display help if [ "$(type -t - "$1" 2>/dev/null)" = function ]; then "$@" else --help fi fi
关于代码的一些评论:
declare -f
枚举所有可用功能,然后通过sed过滤它们,仅显示具有相应前缀的功能。mylib.sh function1
并将其内部翻译为lib_function1
。这是一个留给读者的练习。$1
编写额外的检查。同时,如果您获取库,它将使您的命名空间变得混乱。如果您不喜欢,可以将名称更改为lib_help
,或者在主代码中检查--help
的args并手动调用帮助功能。答案 10 :(得分:3)
我在脚本中看到了这种用法,并认为它是在脚本中调用basename的一个很好的替代品。
oldIFS=$IFS
IFS=/
for basetool in $0 ; do : ; done
IFS=$oldIFS
...
这是代码的替代品:basetool=$(basename $0)
答案 11 :(得分:0)
这里还没有提到的另一种方法是在无限while循环中初始化参数。以下不是最干净的示例,但可以达到目的。
#!/usr/bin/env bash
[ "$1" ] && foo=0 && bar="baz"
while : "${foo=2}" "${bar:=qux}"; do
echo "$foo"
(( foo == 3 )) && echo "$bar" && break
(( foo=foo+1 ))
done