これは私のコードです。
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.
答え2
関数内を含め、シェルでコマンドが必要な任意の場所で関数を定義できます。この関数は、シェルがファイルを解析するときではなく、シェルがその定義を実行するときに定義されることに注意してください。したがって、ユーザーが最初に実行したときにオプション1を選択すると、whenステートメントが呼び出されたときに関数定義が実行されなかったupdate_profile
ため、コードは機能しません。関数が一度実行されると、関数も定義されます。update_name
case
update_name
update_profile
update_name
update_name
の定義を使用する場所の前に移動する必要があります。
update_name
内部を定義することはupdate_profile
特に有用ではありません。これは、最初の実行update_name
までは定義されていませんが、それ以降は引き続き使用できることを意味します。内部でのみ使用できるようにupdate_profile
するには、関数を定義して関数から返される前に呼び出してください。それにもかかわらず、単純なタスクを実行し、すべての機能をグローバルに定義するのと比較すると、実際には何も得られません。update_name
update_profile
unset -f update_name
答え3
はい、これは実際にシェル機能が何であるかを考えるとより明確になります。
POSIX 準拠シェルの場合、関数定義は標準化されます。
2.9.5機能定義コマンド
- ㅏ機能次のように使用されるカスタム名です。簡単なコマンド呼ぶ複合コマンド新しい位置パラメータ。機能が使用されます」機能定義コマンド「...次のように:
fname() compound-command[io-redirect ...]
関数名は
fname
...実装は別の名前空間を維持する必要があります。機能そして変わりやすい。議論複合コマンドを表す複合コマンドで説明されているように複合コマンド。
したがって、本質的に名前と呼ばれるシェル関数は、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
上記のいくつかの注意:
- アップデートには
read
IFSとバックスラッシュの解釈が適用されます。おそらく本当でしょう。IFS= read -r "$1"
しかし、これらの内容をどのように解釈したいのか分かりません。このスコアに関するより多くの情報とより良い情報を得るには、このサイトで他の回答を見つけてください。 - ここに
printf '\033%s...
ある文書の仮定は/dev/tty
VT100互換端末に接続されており、この場合、使用されるエスケープはここにある文書の最後の改行文字が画面に表示されないようにする必要があります。頑丈にtput
使います。そこからman termcap
より多くの情報を入手してください。- ベストはVT100です。キーの組み合わせを表す方法で、キーボードのキーであるかのように何も使用しないか、ドキュメントに直接エスケープ文字を入力できます
printf
。tput
^V{esc}[A^V{esc}@
^V
CONTROL+V
{esc}
ESC
- ベストはVT100です。キーの組み合わせを表す方法で、キーボードのキーであるかのように何も使用しないか、ドキュメントに直接エスケープ文字を入力できます
上記の関数は、引数のセットに基づいて二重ミッションを実行します。 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
問題は、多くのプロジェクトを実行した後に名前の競合を避けるために非常に長い名前を簡単に使用できることです。
だから私がしたのはcdprojectname
(cdsd
プロジェクトと同じようにsd
)プロジェクトディレクトリに移動するだけでなく、プロジェクトに関するすべての開発環境を初期化する機能を使うことです。
私の考えでは、シェル関数を定義する関数を作成できることは本当にクールだと思います。私のエイリアスを使用するときにこれがうまくいくかどうかをテストする必要があります。vim
- はい、確認しましたが、私のトリックは機能しません。
- jaja とにかく、今vimで発行しても動作しません
cdsd
。まだ素晴らしい機能です!
回答に対するコメントは
「答えに「ありがとう」を追加しないでください。評判が十分であれば、役に立つと思う質問と回答に投票できます。 – Jeff Schaller。
申し訳ありません。手紙を書くことはできませんでしたが、感謝の意で書いた内容は書きませんでした。しかし、以前のコメント/回答を見ると、この機能の可能なユースケースは実際には強調されておらず、一部の人にとっては、 「他の関数内に関数を入れ子にすることもできます。しかし、これはあまり役に立ちません。」
感謝挨拶ではありませんが、実際には「あまり役に立ちません」ではなく、他のスクリプト/シェル言語に良い「非常に便利できちんとした機能」であることを指摘したかったです。実際に持ちたい!
非常に役に立たないため、回答を投稿しましたが、現在許可されている回答はこのように提案します...必要な方法で編集するか、そのままにしてください。それ以外の場合は許可された答えだと思います。エラーメッセージ/プロンプトがあります。