関数内に関数を追加することは可能ですか?

関数内に関数を追加することは可能ですか?

これは私のコードです。

function update_profile
{
    echo "1. Update Name"
    echo "2. Update Age"
    echo "3. Update Gender"
    echo "Enter option: "
    read option

    case $option in
         1) update_name ;;
         2) update_age ;;
         3) update_gender ;;
    esac

    function update_name
    {
        echo "Enter new name: "
        read name
    }
}

これが可能かどうかを確認したかったです。すべてのコードをケースに入れることができることはわかっていますが、そうすれば乱雑になるので、関数の内部にスタンドアロン関数を作成し、そのコマンドを実行する必要があるたびに呼び出したいと思います。

答え1

はい、可能です。

あまり役に立ちませんが、他の関数内に関数を入れ子にすることも可能です。

f1 ()
{

  f2 () # nested
  {
    echo "Function \"f2\", inside \"f1\"."
  }

}  

f2  #  Gives an error message.
    #  Even a preceding "declare -f f2" wouldn't help.

echo    

f1  #  Does nothing, since calling "f1" does not automatically call "f2".
f2  #  Now, it's all right to call "f2",
    #+ since its definition has been made visible by calling "f1".

    # Thanks, S.C.

源泉:Linux文書化プロジェクト

答え2

関数内を含め、シェルでコマンドが必要な任意の場所で関数を定義できます。この関数は、シェルがファイルを解析するときではなく、シェルがその定義を実行するときに定義されることに注意してください。したがって、ユーザーが最初に実行したときにオプション1を選択すると、whenステートメントが呼び出されたときに関数定義が実行されなかったupdate_profileため、コードは機能しません。関数が一度実行されると、関数も定義されます。update_namecaseupdate_nameupdate_profileupdate_name

update_nameの定義を使用する場所の前に移動する必要があります。

update_name内部を定義することはupdate_profile特に有用ではありません。これは、最初の実行update_nameまでは定義されていませんが、それ以降は引き続き使用できることを意味します。内部でのみ使用できるようにupdate_profileするには、関数を定義して関数から返される前に呼び出してください。それにもかかわらず、単純なタスクを実行し、すべての機能をグローバルに定義するのと比較すると、実際には何も得られません。update_nameupdate_profileunset -f update_name

答え3

はい、これは実際にシェル機能が何であるかを考えるとより明確になります。

POSIX 準拠シェルの場合、関数定義は標準化されます。

  • 2.9.5機能定義コマンド

    • 機能次のように使用されるカスタム名です。簡単なコマンド呼ぶ複合コマンド新しい位置パラメータ。機能が使用されます」機能定義コマンド「...次のように:

    fname() compound-command[io-redirect ...]

    • 関数名はfname...実装は別の名前空間を維持する必要があります。機能そして変わりやすい

    • 議論複合コマンドを表す複合コマンドで説明されているように複合コマンド

      • いつ。 。 。いつ機能発表されましたが、一つではありません。拡大する存在する単語拡張に基づいている必要がありますcompound-commandまたは<<&io-redirect&>;この関数が呼び出されるたびに、すべてが${expansions}正常に実行されなければなりません。同様にオプション<<&io-redirect&>リダイレクトなどvariable=assignments以内にcompound-command関数定義中ではなく、関数の自己実行中に実行する必要があります。バラよりシェルエラーの結果対話型および非対話型シェルでこれらの操作が失敗すると、結果が表示されます。

したがって、本質的に名前と呼ばれるシェル関数は、fname少なくとも1つの文字列を含むリテラル文字列です。複合コマンドfnameシェルは入力に現れるのではなく、メモリから呼び出され実行されます。コマンド位置- この言葉はいつでもガイドラインします。この定義は、POSIX シェルで関数を使用できる多くの可能性を開きます。次のいずれかの状況でも許可されます。

fn() {
    command; list;
    fn() { : redefines itself upon first execution; }
}

...そして...

fn() {
    helper1() { : defines another function which it can call; }
    helper2() { : and another; }
    helper1 "$@" | helper2 "$@"     #processes args twice over pipe
    command "$@"; list;             #without altering its args
}

しかし、これはほんの一例です。意味を考えると複合コマンドあなたは伝統的なfn() { : cmds; }形だけです一方通行機能が機能します。いくつかの異なるタイプの複合コマンドを考えてみましょう。

 { compound; list; of; commands;} <>i/o <i >o
 (subshelled; compound; list; of; commands) <>i/o <i >o
 if ...; then ...; fi <>i/o <i >o
 case ... in (...) ...;; esac <>i/o <i >o
 for ... [in ... ;] do ...; done <>i/o <i >o
 (while|until) ...; do ....; done <>i/o <i >o

他にも他にもあります。上記の1つは次のとおりです。

 fname() compound list

...そして関数として割り当てられていないときに入れ子にすることができるすべての項目は、コマンドで定義されていてもまだ入れ子にすることができます。

関数を作成する1つの方法は次のとおりです。

update_prof(){
    cat >&3
    read "${2-option}" <&3
    case "${1-$option}" in
    1) update_prof '' name      ;;
    2) update_prof '' age       ;;
    3) update_prof '' gender    ;;
    *) unset option             ;;
    esac
} <<-PROMPT 3<>/dev/tty
${1-
    1. Update Name
    2. Update Age
    3. Update Gender
}
    Enter ${2:-option}: $(
        printf '\033%s' \[A @
)
PROMPT

上記のいくつかの注意:

  1. アップデートにはreadIFSとバックスラッシュの解釈が適用されます。おそらく本当でしょう。IFS= read -r "$1"しかし、これらの内容をどのように解釈したいのか分かりません。このスコアに関するより多くの情報とより良い情報を得るには、このサイトで他の回答を見つけてください。
  2. ここにprintf '\033%s...ある文書の仮定は/dev/ttyVT100互換端末に接続されており、この場合、使用されるエスケープはここにある文書の最後の改行文字が画面に表示されないようにする必要があります。頑丈にtput使います。そこからman termcapより多くの情報を入手してください。
    • ベストはVT100です。キーの組み合わせを表す方法で、キーボードのキーであるかのように何も使用しないか、ドキュメントに直接エスケープ文字を入力できますprintftput^V{esc}[A^V{esc}@^VCONTROL+V{esc}ESC

上記の関数は、引数のセットに基づいて二重ミッションを実行します。 2回行い、必要な場合にのみヒントを再評価します。したがって、2番目の関数は必要ありません。これは、最初に呼び出されたものの両方を実行できるためです。まず、パラメータはありません。

だから私が走ったら...

update_prof; printf %s\\n "$name"

その後のターミナル活動は次のとおりです。

1. Update Name
2. Update Age
3. Update Gender

Enter option: 1

Enter name: yo mama
yo mama

答え4

ここにJeff Schallerの削除された回答をもう一度追加して、ユースケースが存在することを示す回答があることを確認し、「あまり役に立たない」という許可された回答の意味が本質的に間違っていることを確認します。

ここに戻ります(私の答えが何を意味するのかを示すために修正されました)。

「とても便利」ではないですか?私はこれが本当にかなりクールだと思います。

/には、自分の環境を特定のプロジェクトに初期化するbashrcプロジェクト固有の初期化機能があります。bash_aliases

それ以前は、すべてのエイリアスと関数をグローバルに定義しました(このようなショートカットcdprojectname)。modulename_build問題は、多くのプロジェクトを実行した後に名前の競合を避けるために非常に長い名前を簡単に使用できることです。

だから私がしたのはcdprojectnamecdsdプロジェクトと同じようにsd)プロジェクトディレクトリに移動するだけでなく、プロジェクトに関するすべての開発環境を初期化する機能を使うことです。

私の考えでは、シェル関数を定義する関数を作成できることは本当にクールだと思います。私のエイリアスを使用するときにこれがうまくいくかどうかをテストする必要があります。vim

  • はい、確認しましたが、私のトリックは機能しません。
  • jaja とにかく、今vimで発行しても動作しませんcdsd。まだ素晴らしい機能です!

回答に対するコメントは

「答えに「ありがとう」を追加しないでください。評判が十分であれば、役に立つと思う質問と回答に投票できます。 – Jeff Schaller。

申し訳ありません。手紙を書くことはできませんでしたが、感謝の意で書いた内容は書きませんでした。しかし、以前のコメント/回答を見ると、この機能の可能なユースケースは実際には強調されておらず、一部の人にとっては、 「他の関数内に関数を入れ子にすることもできます。しかし、これはあまり役に立ちません。

感謝挨拶ではありませんが、実際には「あまり役に立ちません」ではなく、他のスクリプト/シェル言語に良い「非常に便利できちんとした機能」であることを指摘したかったです。実際に持ちたい!

非常に役に立たないため、回答を投稿しましたが、現在許可されている回答はこのように提案します...必要な方法で編集するか、そのままにしてください。それ以外の場合は許可された答えだと思います。エラーメッセージ/プロンプトがあります。

関連情報