402

何もしないコマンドの目的は何ですか? コメント リーダー以上のものではありませんが、実際にはそれ自体が組み込みのシェルです。

スクリプトにコメントを挿入するよりも、1 回の呼び出しで約 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'
while : ; do command ; done

私が本当に探しているのは、それがどのような歴史的用途を持っていたのかということだと思います.

4

12 に答える 12

471

歴史的に、Bourne シェルには組み込みコマンドとしてtrueandがありませんでした。代わりに、単に、およびのようなものにエイリアスされていました。falsetrue:falselet 0

:true古代の Bourne 由来のシェルへの移植性よりもわずかに優れています。簡単な例として、!パイプライン演算子も||リスト演算子も持たないことを考えてみましょう (いくつかの古い Bourne シェルの場合のように)。これにより、終了ステータスに基づいて分岐するための唯一の手段としてステートメントのelse句が残ります。if

if command; then :; else ...; fi

if空でないthen句が必要であり、コメントは空でないものとしてカウントされないため:、ノーオペレーションとして機能します。

現在(つまり、現代のコンテキストでは)、通常は または のいずれ:かを使用できますtrue。どちらも POSIX で指定されており、true読みやすいものもあります。しかし興味深い違いが1:あります。true

  • シェルに組み込むには、特別なビルトインが必要です。通常のビルトインは「典型的に」組み込まれているだけですが、厳密には保証されていません。通常、ほとんどのシステムの PATH には:、 の機能で名前が付けられた通常のプログラムがあってはなりません。true

  • おそらく最も重要な違いは、特別なビルトインを使用すると、ビルトインによって設定された変数は、単純なコマンド評価中の環境であっても、コマンドが完了した後も保持されることです。

    $ unset x; ( x=hi :; echo "$x" )
    hi
    $ ( x=hi true; echo "$x" )
    
    $
    

    Zsh は、POSIX 互換モードで動作している場合を除き、GNU Bash と同様にこの要件を無視しますが、ダッシュ、ksh93、および mksh を含む他のすべての主要な「POSIX sh 派生」シェルはこれを観察します。

  • もう 1 つの違いは、通常のビルトインは互換性がなければならないということexecです。ここでは Bash を使用して示します。

    $ ( exec : )
    -bash: exec: :: not found
    $ ( exec true )
    $
    
  • POSIX は、:が よりも高速である可能性があることも明示的に示してtrueいますが、これはもちろん実装固有の詳細です。

于 2010-07-11T22:42:16.007 に答える
83

変数コマンドを簡単に有効/無効にするために使用します。

#!/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 であることを保証する最も簡単な方法の 1 つです。

于 2010-07-12T05:15:47.593 に答える
70

の便利なアプリケーション:は、実際に結果をコマンドに渡すのではなく、副作用のためにパラメーター展開を使用することにのみ関心がある場合です。

その場合、0 または 1 の終了ステータスが必要かどうかに応じて、:またはの引数としてパラメータ展開を使用します。例は次のようになります。false

: "${var:=$1}"

組み込みなので:、かなり高速です。

于 2011-02-03T22:36:25.943 に答える
59

:ブロック コメントにも使用できます (C 言語の /* */ に似ています)。たとえば、スクリプト内のコード ブロックをスキップする場合は、次のようにします。

: << 'SKIP'

your code block here

SKIP
于 2013-09-21T07:15:28.897 に答える
36

passこれは Pythonの場合と似ています。

1 つの使用法は、関数が書き込まれるまでスタブ化することです。

future_function () { :; }
于 2010-07-11T22:55:45.630 に答える
36

他の回答で言及されていないさらに2つの用途:

ロギング

次のスクリプト例を見てください。

set -x
: Logging message here
example_command

最初の行set -xは、シェルがコマンドを実行する前に出力するようにします。これは非常に便利な構造です。欠点は、通常のecho Log messageタイプのステートメントがメッセージを 2 回出力することです。コロン メソッドはそれを回避します。の場合と同様に、特殊文字をエスケープする必要があることに注意してくださいecho

Cronの役職

次のように、cron ジョブで使用されているのを見てきました。

45 10 * * * : Backup for database ; /opt/backup.sh

/opt/backup.shこれは、毎日 10:45にスクリプトを実行する cron ジョブです。この手法の利点は、/opt/backup.sh出力を印刷するときに電子メールの件名の見栄えが良くなることです。

于 2015-04-24T10:33:23.633 に答える
35

ログをクリアするのに役立つ、ファイルをゼロバイトに切り詰めたい場合は、これを試してください:

:> file.log
于 2011-06-23T16:27:39.333 に答える
26

次のように、バッククォート ( ) と組み合わせて``、出力を表示せずにコマンドを実行できます。

: `some_command`

もちろん、そのまま実行することもできますsome_command > /dev/nullが、:-version はやや短くなっています。

そうは言っても、人々を混乱させるだけなので、実際にそれを行うことはお勧めしません。可能なユースケースとして頭に浮かんだだけです。

于 2010-07-11T22:43:10.867 に答える
18

多言語プログラムにも役立ちます。

#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }

これは、実行可能なシェル スクリプトJavaScript プログラムの両方./filename.jssh filename.jsなりましたnode filename.js

(確かに少し変わった使い方ですが、それでも効果的です。)


要求に応じて、いくつかの説明:

  • シェルスクリプトは行ごとに評価されます。コマンドが実行されるとexec、シェルが終了し、そのプロセスが結果のコマンドに置き換えられます。これは、シェルにとって、プログラムが次のように見えることを意味します。

    #!/usr/bin/env sh
    ':' //; exec "$(command -v node)" "$0" "$@"
    
  • 単語内でパラメーターの展開やエイリアシングが発生していない限り、シェル スクリプト内単語は、その意味を変更することなく引用符で囲むことができます。これは、':'と同等である:ことを意味します (以下で説明する JavaScript セマンティクスを実現するために、ここでは引用符で囲んだだけです)。

  • ...そして、上記のように、最初の行の最初のコマンドはノーオペレーションです (これは、 に変換される: //か、単語を引用したい場合は、に変換されます。 JavaScript の場合のように、ここでは特別な意味を持たない':' '//'ことに注意してください。//意味のない言葉が捨てられているだけです。)

  • 最後に、最初の行の 2 番目のコマンド (セミコロンの後ろ) は、プログラムの真髄です。これは、呼び出されるシェル スクリプトを、残りのスクリプトを評価するために呼び出される Node.js プロセスにexec置き換える呼び出しです。

  • 一方、JavaScript の最初の行は文字列リテラル ( ':') として解析され、次にコメントが削除されます。したがって、JavaScript にとって、プログラムは次のようになります。

    ':'
    ~function(){ ... }
    

    文字列リテラルはそれ自体が 1 行にあるため、ノーオペレーション ステートメントであり、プログラムから削除されます。つまり、行全体が削除され、プログラム コード (この例では本体)だけが残ります。function(){ ... }

于 2015-12-30T17:14:14.000 に答える
17

自己文書化関数

:を使用して、ドキュメントを関数に埋め込むこともできます。

mylib.shさまざまな機能を提供するライブラリ script があるとします。ライブラリをソースして ( )、. mylib.shその直後に関数を呼び出す ( lib_function1 arg1 arg2) か、名前空間が乱雑になるのを避けて、関数引数を使用してライブラリを呼び出す( ) ことができますmylib.sh lib_function1 arg1 arg2

mylib.sh --helpヘルプ テキストで関数リストを手動で管理することなく、使用可能な関数とその使用法のリストを入力して取得できると便利だと思いませんか?

#!/ビン/バッシュ

# すべての「パブリック」関数は、このプレフィックスで開始する必要があります
LIB_PREFIX='lib_'

# 「パブリック」ライブラリ関数
lib_function1() {
    : この関数は、2 つの引数で複雑なことを行います。
    :
    : パラメーター:
    : ' arg1 - 最初の引数 ($1)'
    : ' arg2 - 2 番目の引数'
    :
    : 結果:
    : " それは複雑です"

    # 実際の関数コードはここから
}

lib_function2() {
    : 関数ドキュメント

    # ここに関数コード
}

# ヘルプ機能
- ヘルプ() {
    echo MyLib v0.0.1
    エコー
    echo 使用法: mylib.sh [関数名 [引数]]
    エコー
    echo 利用可能な機能:
    宣言 -f | sed -n -e '/^'$LIB_PREFIX'/,/^}$/{/\(^'$LIB_PREFIX'\)\|\(^[ \t]*:\)/{
        s/^\('$LIB_PREFIX'.*\) ()/\n=== \1 ===/;s/^[ \t]*: \?['\''"]\?/ / ;s/['\''"]\?;\?$//;p}}'
}

# メインコード
if [ "${BASH_SOURCE[0]}" = "${0}" ]; それから
    # スクリプトはソースではなく実行されました
    # 要求された機能を呼び出すか、ヘルプを表示する
    if [ "$(type -t - "$1" 2>/dev/null)" = 関数]; それから
        「$@」
    そうしないと
        - ヘルプ
    フィ
フィ

コードに関するいくつかのコメント:

  1. すべての「パブリック」関数には同じプレフィックスがあります。これらのみが、ユーザーによって呼び出され、ヘルプ テキストにリストされることを意図しています。
  2. 自己文書化機能は前のポイントに依存し、使用declare -f可能なすべての関数を列挙し、sed を介してそれらをフィルター処理して、適切なプレフィックスを持つ関数のみを表示します。
  3. 不要な拡張や空白の削除を防ぐために、ドキュメントを一重引用符で囲むことをお勧めします。テキストでアポストロフィ/引用符を使用する場合も注意が必要です。
  4. ライブラリ プレフィックスを内部化するコードを書くことができます。つまり、ユーザーは入力するだけで、mylib.sh function1内部的に に変換されlib_function1ます。これは読者に委ねられた演習です。
  5. ヘルプ機能の名前は「--help」です。これは、ライブラリ呼び出しメカニズムを使用してヘルプ自体を表示する便利な (つまり、遅延) アプローチであり、追加のチェックをコーディングする必要はありません$1。同時に、ライブラリをソースすると名前空間が乱雑になります。それが気に入らない場合は、名前を次のように変更するか、lib_help実際に--helpメイン コードで引数を確認してヘルプ関数を手動で呼び出すことができます。
于 2016-03-28T15:28:39.710 に答える
4

スクリプトでこの使用法を見て、スクリプト内で basename を呼び出す代わりに使用できると考えました。

oldIFS=$IFS  
IFS=/  
for basetool in $0 ; do : ; done  
IFS=$oldIFS  

...これはコードの置き換えです:basetool=$(basename $0)

于 2016-04-29T23:29:59.717 に答える
2

ここでまだ言及されていない別の方法は、無限 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
于 2020-07-20T09:30:35.830 に答える