amp*_*ine 313 bash shell built-in
命令的目的是什么,什么都不做,只是一个评论领导者,但实际上是一个内置的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
Run Code Online (Sandbox Code Playgroud)
我想我真正想要的是它可能具有的历史应用.
ear*_*arl 392
从历史上看,炮弹伯恩没有true和false内置的命令.true而是简单地别名:,和false类似的东西let 0.
:比true古代Bourne衍生的贝壳的便携性要好一些.举个简单的例子,考虑既没有!管道运算符也没有||列表运算符(就像一些古老的Bourne shell一样).这使得语句的else子句if成为基于退出状态进行分支的唯一方法:
if command; then :; else ...; fi
Run Code Online (Sandbox Code Playgroud)
由于if需要非空then子句并且注释不计为非空,因此:用作无操作.
如今(即:在现代语境中)你通常可以使用:或者true.两者都由POSIX指定,有些true更容易阅读.然而,有一个有趣的区别::是所谓的POSIX 特殊内置,而是true一个常规的内置.
外壳中需要安装特殊的内置插件; 常规内置插件仅"内置",但并未严格保证.通常不应该有一个以大多数系统的PATH :功能命名的常规程序true.
可能最重要的区别在于,使用特殊的内置函数,内置的任何变量 - 即使在简单命令评估期间的环境中 - 在命令完成后仍然存在,如此处使用ksh93所示:
$ unset x; ( x=hi :; echo "$x" )
hi
$ ( x=hi true; echo "$x" )
$
Run Code Online (Sandbox Code Playgroud)
请注意,Zsh忽略了这个要求,GNU Bash也是如此,除非在POSIX兼容模式下运行,但所有其他主要的"POSIX sh派生"shell都会观察到这一点,包括dash,ksh93和mksh.
另一个区别是常规内置函数必须兼容exec- 在这里使用Bash演示:
$ ( exec : )
-bash: exec: :: not found
$ ( exec true )
$
Run Code Online (Sandbox Code Playgroud)POSIX也明确指出:可能比true这更快,尽管这当然是一个特定于实现的细节.
Kev*_*tle 61
我用它来轻松启用/禁用变量命令:
#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
vecho=":" # no "verbose echo"
else
vecho=echo # enable "verbose echo"
fi
$vecho "Verbose echo is ON"
Run Code Online (Sandbox Code Playgroud)
从而
$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON
Run Code Online (Sandbox Code Playgroud)
这使得一个干净的脚本.这不能用'#'来完成.
也,
: >afile
Run Code Online (Sandbox Code Playgroud)
是保证'afile'存在但是长度为0的最简单方法之一.
orm*_*aaj 51
一个有用的应用程序:如果你只对使用参数扩展的副作用感兴趣,而不是实际将结果传递给命令.在这种情况下,您使用PE作为参数:或者取决于您是否希望退出状态为0或1.示例可能是: "${var:=$1}".既然:是内置的,它应该非常快.
小智 44
:也可以用于块注释(类似于C语言中的/**/).例如,如果要跳过脚本中的代码块,可以执行以下操作:
: << 'SKIP'
your code block here
SKIP
Run Code Online (Sandbox Code Playgroud)
Ahi*_*una 31
如果您要将文件截断为零字节,对清除日志很有用,请尝试以下操作:
:> file.log
Run Code Online (Sandbox Code Playgroud)
Pau*_*ce. 29
它与passPython 类似.
一种用法是将函数存根直到它被写入:
future_function () { :; }
Run Code Online (Sandbox Code Playgroud)
Fli*_*imm 28
在其他答案中没有提到的另外两个用途:
以此示例脚本为例:
set -x
: Logging message here
example_command
Run Code Online (Sandbox Code Playgroud)
第一set -x行使shell在运行之前打印出命令.这是一个非常有用的结构.缺点是通常echo Log message的语句类型现在打印消息两次.冒号方法绕过那个.请注意,您仍然必须像您一样逃避特殊字符echo.
我已经看到它被用在cron作业中,像这样:
45 10 * * * : Backup for database ; /opt/backup.sh
Run Code Online (Sandbox Code Playgroud)
这是一个cron作业,/opt/backup.sh每天10:45 运行脚本.这种技术的优点在于,当/opt/backup.sh打印输出时,它可以更好地查看电子邮件主题.
sep*_*p2k 21
您可以将它与backticks(``)结合使用来执行命令而不显示其输出,如下所示:
: `some_command`
Run Code Online (Sandbox Code Playgroud)
当然你可以这么做some_command > /dev/null,但是:-version有点短.
话虽如此,我不建议实际这样做,因为它会让人感到困惑.它只是作为一个可能的用例而浮现在脑海中.
ELL*_*BLE 14
它对多语言程序也很有用:
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }
Run Code Online (Sandbox Code Playgroud)
现在,这既是一个可执行的shell脚本和 JavaScript程序:含义./filename.js,sh filename.js和node filename.js所有的工作.
(绝对有点奇怪的用法,但仍然有效.)
根据要求进行一些解释:
Shell脚本逐行评估; 并且该exec命令在运行时终止shell并用结果命令替换它的进程.这意味着对于shell,程序看起来像这样:
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
Run Code Online (Sandbox Code Playgroud)只要单词中没有出现参数扩展或别名,shell脚本中的任何单词都可以用引号括起来而不改变其含义; 这意味着它':'等同于:(我们只在这里用引号将其包装起来以实现下面描述的JavaScript语义)
...并且如上所述,第一行上的第一个命令是no-op(它转换为: //,或者如果你更喜欢引用单词,':' '//'请注意,//这里没有特殊含义,就像在JavaScript中一样;这只是一个毫无意义的词,被扔掉了.)
最后,第一行(分号后)的第二个命令是程序的真正含义:它是exec调用替换被调用的shell脚本的调用,调用Node.js进程来评估脚本的其余部分.
同时,JavaScript中的第一行解析为string-literal(':'),然后删除注释,删除; 因此,对于JavaScript,程序看起来像这样:
':'
~function(){ ... }
Run Code Online (Sandbox Code Playgroud)
由于string-literal本身就是一行,所以它是一个无操作语句,因此从程序中删除; 这意味着,整个行被删除,留下仅你的程序代码(在本例中,function(){ ... }身体).
Sir*_*hos 12
您还可以使用:在函数中嵌入文档.
假设您有一个库脚本mylib.sh,提供各种功能.您可以获取库(. mylib.sh)并在该(lib_function1 arg1 arg2)之后直接调用函数,或者避免使命名空间混乱并使用函数参数(mylib.sh lib_function1 arg1 arg2)调用库.
如果您还可以键入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 并手动调用帮助功能.小智 5
我在脚本中看到了这种用法,并认为它是在脚本中调用基本名称的一个很好的替代品。
oldIFS=$IFS
IFS=/
for basetool in $0 ; do : ; done
IFS=$oldIFS
Run Code Online (Sandbox Code Playgroud)
...这是代码的替换:basetool=$(basename $0)