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
它与pass
Python 类似.
一种用法是将函数存根直到它被写入:
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)