:(冒号)GNU Bash 内置的目的是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3224878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the purpose of the : (colon) GNU Bash builtin?
提问by amphetamachine
What is the purpose of a command that does nothing, being little more than a comment leader, but is actually a shell builtin in and of itself?
一个什么都不做的命令的目的是什么,只不过是一个评论领导者,但实际上是一个内置的shell?
It's slower than inserting a comment into your scripts by about 40% per call, which probably varies greatly depending on the size of the comment. The only possible reasons I can see for it are these:
每次调用时,它比在脚本中插入注释慢 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
I guess what I'm really looking for is what historical application it might have had.
我想我真正要寻找的是它可能具有的历史应用。
回答by earl
Historically, Bourne shells didn't have true
and false
as built-in commands. true
was instead simply aliased to :
, and false
to something like let 0
.
从历史上看,Bourne shell 没有true
和false
作为内置命令。true
取而代之的是简单地别名为:
, 和false
类似的东西let 0
。
:
is slightly better than true
for portability to ancient Bourne-derived shells. As a simple example, consider having neither the !
pipeline operator nor the ||
list operator (as was the case for some ancient Bourne shells). This leaves the else
clause of the if
statement as the only means for branching based on exit status:
:
略好于true
古代 Bourne 衍生壳的可移植性。作为一个简单的例子,考虑既没有!
管道操作符也没有||
列表操作符(一些古老的 Bourne shell 就是这种情况)。这使得语句的else
子句if
成为基于退出状态进行分支的唯一方法:
if command; then :; else ...; fi
Since if
requires a non-empty then
clause and comments don't count as non-empty, :
serves as a no-op.
由于if
需要非空then
子句并且注释不算作非空,因此:
用作空操作。
Nowadays(that is: in a modern context) you can usually use either :
or true
. Both are specified by POSIX, and some find true
easier to read. However there is one interesting difference: :
is a so-called POSIX special built-in, whereas true
is a regular built-in.
如今(即:在现代语境中)您通常可以使用:
或true
。两者都由 POSIX 指定,有些true
更容易阅读。然而,有一个有趣的区别::
是所谓的 POSIX特殊内置,而true
是常规内置。
Special built-ins are required to be built into the shell; Regular built-ins are only "typically" built in, but it isn't strictly guaranteed. There usually shouldn't be a regular program named
:
with the function oftrue
in PATH of most systems.Probably the most crucial difference is that with special built-ins, any variable set by the built-in - even in the environment during simple command evaluation - persists after the command completes, as demonstrated here using ksh93:
$ unset x; ( x=hi :; echo "$x" ) hi $ ( x=hi true; echo "$x" ) $
Note that Zsh ignores this requirement, as does GNU Bash except when operating in POSIX compatibility mode, but all other major "POSIX sh derived" shells observe this including dash, ksh93, and mksh.
Another difference is that regular built-ins must be compatible with
exec
- demonstrated here using Bash:$ ( exec : ) -bash: exec: :: not found $ ( exec true ) $
POSIX also explicitly notes that
:
may be faster thantrue
, though this is of course an implementation-specific detail.
需要在 shell 中内置特殊的内置函数;常规内置插件只是“通常”内置的,但并没有严格保证。大多数系统通常不应该有一个以in PATH
:
函数命名的常规程序true
。可能最关键的区别在于,对于特殊的内置函数,内置函数设置的任何变量 - 即使在简单命令评估期间的环境中 - 在命令完成后仍然存在,如此处使用 ksh93 所示:
$ unset x; ( x=hi :; echo "$x" ) hi $ ( x=hi true; echo "$x" ) $
请注意,除了在 POSIX 兼容模式下运行时,Zsh 和 GNU Bash 一样忽略了这一要求,但所有其他主要的“POSIX sh 派生”shell 都遵守这一点,包括 dash、ksh93 和 mksh。
另一个区别是常规内置函数必须兼容
exec
- 此处使用 Bash 进行演示:$ ( exec : ) -bash: exec: :: not found $ ( exec true ) $
POSIX 还明确指出
:
可能比 快true
,尽管这当然是特定于实现的细节。
回答by Kevin Little
I use it to easily enable/disable variable commands:
我用它来轻松启用/禁用变量命令:
#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
vecho=":" # no "verbose echo"
else
vecho=echo # enable "verbose echo"
fi
$vecho "Verbose echo is ON"
Thus
因此
$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON
This makes for a clean script. This cannot be done with '#'.
这使得脚本干净。这不能用“#”来完成。
Also,
还,
: >afile
is one of the simplest ways to guarantee that 'afile' exists but is 0 length.
是保证 'afile' 存在但长度为 0 的最简单方法之一。
回答by ormaaj
A useful application for : is if you're only interested in using parameter expansions for their side-effects rather than actually passing their result to a command. In that case you use the PE as an argument to either : or false depending upon whether you want an exit status of 0 or 1. An example might be : "${var:=$1}"
. Since :
is a builtin it should be pretty fast.
一个有用的应用 : 是如果您只对使用参数扩展产生副作用而不是实际将其结果传递给命令感兴趣。在这种情况下,您可以使用 PE 作为 : 或 false 的参数,具体取决于您希望退出状态为 0 还是 1。一个例子可能是: "${var:=$1}"
. 由于:
是内置的,它应该很快。
回答by zagpoint
:
can also be for block comment (similar to /* */ in C language). For example, if you want to skip a block of code in your script, you can do this:
:
也可以用于块注释(类似于C语言中的/* */)。例如,如果您想跳过脚本中的一段代码,您可以这样做:
: << 'SKIP'
your code block here
SKIP
回答by Ahi Tuna
If you'd like to truncate a file to zero bytes, useful for clearing logs, try this:
如果您想将文件截断为零字节,这对清除日志很有用,请尝试以下操作:
:> file.log
回答by Paused until further notice.
It's similar to pass
in Python.
它类似于pass
在 Python 中。
One use would be to stub out a function until it gets written:
一种用途是在函数被写入之前存根:
future_function () { :; }
回答by Flimm
Two more uses not mentioned in other answers:
其他答案中未提及的另外两个用途:
Logging
日志记录
Take this example script:
以这个示例脚本为例:
set -x
: Logging message here
example_command
The first line, set -x
, makes the shell print out the command before running it. It's quite a useful construct. The downside is that the usual echo Log message
type of statement now prints the message twice. The colon method gets round that. Note that you'll still have to escape special characters just like you would for echo
.
第一行 ,set -x
使 shell 在运行之前打印出命令。这是一个非常有用的构造。缺点是通常echo Log message
的语句类型现在将消息打印两次。冒号方法解决了这个问题。请注意,您仍然需要像转义echo
.
Cron job titles
Cron 职位
I've seen it being used in cron jobs, like this:
我已经看到它在 cron 工作中使用,如下所示:
45 10 * * * : Backup for database ; /opt/backup.sh
This is a cron job that runs the script /opt/backup.sh
every day at 10:45. The advantage of this technique is that it makes for better looking email subjects when the /opt/backup.sh
prints some output.
这是一个/opt/backup.sh
每天 10:45运行脚本的 cron 作业。这种技术的优点是当/opt/backup.sh
打印一些输出时它可以使电子邮件主题看起来更好。
回答by sepp2k
You could use it in conjunction with backticks (``
) to execute a command without displaying its output, like this:
您可以将它与反引号 ( ``
)结合使用来执行命令而不显示其输出,如下所示:
: `some_command`
Of course you could just do some_command > /dev/null
, but the :
-version is somewhat shorter.
当然你可以只做some_command > /dev/null
,但:
-version 有点短。
That being said I wouldn't recommend actually doing that as it would just confuse people. It just came to mind as a possible use-case.
话虽如此,我不建议实际这样做,因为它只会使人们感到困惑。它只是作为一个可能的用例出现在脑海中。
回答by ELLIOTTCABLE
It's also useful for polyglot programs:
它对多语言程序也很有用:
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "#!/usr/bin/env sh
':' //; exec "$(command -v node)" "':'
~function(){ ... }
" "$@"
" "$@"
~function(){ ... }
This is now both an executable shell-script anda JavaScript program: meaning ./filename.js
, sh filename.js
, and node filename.js
all work.
这现在既是一个可执行的 shell 脚本又是一个 JavaScript 程序:意思是./filename.js
、sh filename.js
、 和node filename.js
所有的工作。
(Definitely a little bit of a strange usage, but effective nonetheless.)
(绝对有点奇怪的用法,但仍然有效。)
Some explication, as requested:
根据要求进行一些解释:
Shell-scripts are evaluated line-by-line; and the
exec
command, when run, terminates the shell and replacesit's process with the resultant command. This means that to the shell, the program looks like this:#!/usr/bin/env sh ':' //; exec "$(command -v node)" "
" "$@"':' ~function(){ ... }
As long as no parameter expansion or aliasing is occurring in the word, anyword in a shell-script can be wrapped in quotes without changing its' meaning; this means that
':'
is equivalent to:
(we've only wrapped it in quotes here to achieve the JavaScript semantics described below)... and as described above, the first command on the first line is a no-op (it translates to
: //
, or if you prefer to quote the words,':' '//'
. Notice that the//
carries no special meaning here, as it does in JavaScript; it's just a meaningless word that's being thrown away.)Finally, the second command on the first line (after the semicolon), is the real meat of the program: it's the
exec
call which replaces the shell-script being invoked, with a Node.js process invoked to evaluate the restof the script.Meanwhile, the first line, in JavaScript, parses as a string-literal (
':'
), and then a comment, which is deleted; thus, to JavaScript, the program looks like this:#!/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 ()' : ' 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=== ===/;s/^[ \t]*: \?['\''"]\?/ /;s/['\''"]\?;\?$//;p}}' } # main code if [ "${BASH_SOURCE[0]}" = "##代码##" ]; then # the script was executed instead of sourced # invoke requested function or display help if [ "$(type -t - "" 2>/dev/null)" = function ]; then "$@" else --help fi fi
Since the string-literal is on a line by itself, it is a no-op statement, and is thus stripped from the program; that means that the entire line is removed, leaving onlyyour program-code (in this example, the
function(){ ... }
body.)
Shell 脚本是逐行计算的;并且该
##代码##exec
命令在运行时会终止 shell 并用结果命令替换它的进程。这意味着对于 shell,程序看起来是这样的:只要单词中没有出现参数扩展或别名,shell 脚本中的任何单词都可以用引号括起来而不改变其含义;这意味着它
':'
等同于:
(我们在这里只用引号将它包裹起来以实现下面描述的 JavaScript 语义)... 如上所述,第一行的第一个命令是一个空操作(它转换为
: //
,或者如果您更喜欢引用这些词,':' '//'
。请注意,//
这里没有特殊含义,就像在 JavaScript 中一样;这只是一个被扔掉的无意义的词。)最后,第一行(分号之后)的第二个命令是程序的真正核心:它是
exec
调用替换shell-script 的调用,调用 Node.js 进程来评估脚本的其余部分。同时,在 JavaScript 中,第一行解析为字符串文字 (
##代码##':'
),然后是注释,该注释被删除;因此,对于 JavaScript,程序看起来是这样的:由于字符串文字本身就在一行上,因此它是一个无操作语句,因此从程序中删除;这意味着,整个行被删除,留下仅你的程序代码(在本例中,
function(){ ... }
身体)。
回答by Sir Athos
Self-documenting functions
自记录功能
You can also use :
to embed documentation in a function.
您还可以使用:
在函数中嵌入文档。
Assume you have a library script mylib.sh
, providing a variety of functions. You could either source the library (. mylib.sh
) and call the functions directly after that (lib_function1 arg1 arg2
), or avoid cluttering your namespace and invoke the library with a function argument (mylib.sh lib_function1 arg1 arg2
).
假设您有一个库脚本mylib.sh
,提供多种功能。您可以获取库 ( . mylib.sh
) 并在此之后直接调用函数 ( lib_function1 arg1 arg2
),或者避免混淆命名空间并使用函数参数 ( mylib.sh lib_function1 arg1 arg2
)调用库。
Wouldn't it be nice if you could also type mylib.sh --help
and get a list of available functions and their usage, without having to manually maintain the function list in the help text?
如果您还可以键入mylib.sh --help
并获取可用函数及其用法的列表,而不必手动维护帮助文本中的函数列表,那不是很好吗?
A few comments about the code:
关于代码的一些评论:
- All "public" functions have the same prefix. Only these are meant to be invoked by the user, and to be listed in the help text.
- The self-documenting feature relies on the previous point, and uses
declare -f
to enumerate all available functions, then filters them through sed to only display functions with the appropriate prefix. - It is a good idea to enclose the documentation in single quotes, to prevent undesired expansion and whitespace removal. You'll also need to be careful when using apostrophes/quotes in the text.
- You could write code to internalize the library prefix, i.e. the user only has to type
mylib.sh function1
and it gets translated internally tolib_function1
. This is an exercise left to the reader. - The help function is named "--help". This is a convenient (i.e. lazy) approach that uses the library invoke mechanism to display the help itself, without having to code an extra check for
$1
. At the same time, it will clutter your namespace if you source the library. If you don't like that, you can either change the name to something likelib_help
or actually check the args for--help
in the main code and invoke the help function manually.
- 所有“公共”函数都具有相同的前缀。只有这些旨在由用户调用,并列在帮助文本中。
- self-documenting 特性依赖于前一点,
declare -f
用于枚举所有可用函数,然后通过 sed 过滤它们以仅显示具有适当前缀的函数。 - 将文档括在单引号中是一个好主意,以防止意外的扩展和空格删除。在文本中使用撇号/引号时还需要小心。
- 您可以编写代码来内部化库前缀,即用户只需键入
mylib.sh function1
并在内部将其转换为lib_function1
. 这是留给读者的练习。 - 帮助函数被命名为“--help”。这是一种方便(即懒惰)的方法,它使用库调用机制来显示帮助本身,而无需对
$1
. 同时,如果您使用该库,它会使您的命名空间变得混乱。如果您不喜欢那样,您可以将名称更改为类似名称lib_help
或实际检查--help
主代码中的 args并手动调用帮助功能。