.. 7-language-examples .. index:: single: osh .. _label7: 7. さらなる言語例 ======================================= .. In this section, we'll explore the core language through a series of examples (examples of the build system are the topic of the Chapter 3). この章では、一連の例を通して言語の核心へと迫ります(ビルドシステムのサンプルについては :ref:`label3` を参照してください)。 .. For most of these examples, we'll use the osh command interpreter. For simplicity, the values printed by osh have been abbreviated. 私たちはこれらの例のほとんどに ``osh`` インタープリターを用いています。また、簡単にするため、 ``osh`` によって出力された値は省略されています。 .. _label7.1: 7.1 文字列と配列 --------------------------------------- .. The basic OMake values are strings, sequences, and arrays of values. Sequences are like arrays of values separated by whitespace; the sequences are split on demand by functions that expect arrays. OMakeの基本となる型は文字列とシーケンス、そして値からなる配列です。シーケンスはホワイトスペースによって分割された配列のようなもので、関数の要求に応じて分割されます。 :: osh> X = 1 2 - : "1 2" : Sequence osh> addsuffix(.c, $X) - : : Array .. Sometimes you want to define an array explicitly. For this, use the [] brackets after the variable name, and list each array entry on a single indented line. 時々あなたは明示的に配列を定義したいと思うでしょう。この場合、 ``[]`` を変数名の後に追加し、配列の成分をそれぞれインデントされた行に書くことで実現できます。 :: osh> A[] = Hello world $(getenv HOME) - : : Array .. One central property of arrays is that whitespace in the elements is significant. This can be useful, especially for filenames that contain whitespace. 配列は成分にホワイトスペースを含めることができます。これは配列の主要な特徴の一つであり、重要な役割を担います。また、これはホワイトスペースを含むファイル名にとって特に役立ちます。 :: # ディレクトリ中の現在のファイルを並べる osh> ls -Q "fee" "fi" "foo" "fum" osh> NAME[] = Hello world - : : Array osh> touch $(NAME) osh> ls -Q "fee" "fi" "foo" "fum" "Hello world" .. _label7.2: 7.2 クオート文字列 --------------------------------------- .. A String is a single value; whitespace is significant in a string. Strings are introduced with quotes. There are four kinds of quoted elements; the kind is determined by the opening quote. The symbols ' (single-quote) and " (double-quote) introduce the normal shell-style quoted elements. The quotation symbols are included in the result string. Variables are always expanded within a quote of this kind. Note that the osh(1) (Chapter 15) printer escapes double-quotes within the string; these are only for printing, they are not part of the string itself. ``String`` は単一の値です。文字列の中でホワイトスペースは重要な意味を持っています。文字列をクオートするには4通りの方法があります。まず一番目にクオートをつけることが挙げられるでしょう。シンボル ``'`` (シングルクオート)と ``"`` (ダブルクオート)を用いることで、シェルを扱うときのように成分をクオートすることができます。クオーテーションシンボルは結果の文字列に **含まれます** 。変数は常に内部にクオートを含んだ状態で展開されます。 ``osh(1)`` (15章で説明)は文字列にダブルクオートをつけて出力しますが、これは出力時だけであり、文字列の中には含まれていないことに注意してください。 :: osh> A = 'Hello "world"' - : "'Hello \"world\"'" : String osh> B = "$(A)" - : "\"'Hello \"world\"'\"" : String osh> C = 'Hello \'world\'' - : "'Hello 'world''" : String .. A second kind of quote is introduced with the $' and $" quotes. The number of opening and closing quote symbols is arbitrary. These quotations have several properties: * The quote delimiters are not part of the string. * Backslash \ symbols within the string are treated as normal characters. * The strings may span several lines. * Variables are expanded within $" sequences, but not within $' sequences. 二番目の方法は ``$'`` と ``$"`` クオートを導入することです。始まりと終わりのクオートシンボルの数は任意です。また、これらのクオーテーションはいくつかの性質をもっています。 * クオートデリミタは文字列の一部では **ありません** 。 * 文字列中のバックスラッシュ ``\`` 文字は通常の文字として扱われます。 * 文字列は何行にわたって書くこともできます。 * 変数は ``$"`` を用いて展開することができます。ただし、 ``$'`` では展開できません。 :: osh> A = $'''Here $(IS) an '''' \(example\) string[''' - : "Here $(IS) an '''' \\(example\\) string[" : String osh> B = $""""A is "$(A)" """" - : "A is \"Here $(IS) an '''' \\(example\\) string[\" " : String osh> value $(A.length) - : 38 : Int osh> value $(A.nth 5) - : "$" : String osh> value $(A.rev) - : "[gnirts )\\elpmaxe(\\ '''' na )SI($ ereH" : String .. Strings and sequences both have the property that they can be merged with adjacent non-whitespace text. 文字列とシーケンスの両方はホワイトスペースが含まれていない文字列とくっつけることができます。 :: osh> A = a b c - : "a b c" : Sequence osh> B = $(A).c - : : Sequence osh> value $(nth 2, $(B)) - : "c.c" : String osh> value $(length $(B)) - : 3 : Int .. Arrays are different. The elements of an array are never merged with adjacent text of any kind. Arrays are defined by adding square brackets [] after a variable name and defining the elements with an indented body. The elements may include whitespace. 配列は異なります。配列の成分はどのような方法を用いても文字列とくっつけることができません。配列は括弧 ``[]`` を変数名につけ、インデントした内容を記述することで成分を定義できます。各成分にはホワイトスペースを含めることもできます。 :: osh> A[] = a b foo bar - : : Array osh> echo $(A).c a b foo bar .c osh> value $(A.length) - : 2 : Int osh> value $(A.nth 1) - : "foo bar" : Sequence .. Arrays are quite helpful on systems where filenames often contain whitespace. 配列はしばしばシステム上にホワイトスペースを含んだファイル名を使う場合において、非常に有用なツールとなります。 :: osh> FILES[] = c:\Documents and Settings\jyh\one file c:\Program Files\omake\second file osh> CFILES = $(addsuffix .c, $(FILES)) osh> echo $(CFILES) c:\Documents and Settings\jyh\one file.c c:\Program Files\omake\second file.c .. _label7.3: 7.3 ファイルとディレクトリ --------------------------------------- .. OMake projects usually span multiple directories, and different parts of the project execute commands in different directories. There is a need to define a location-independent name for a file or directory. 複数のディレクトリにまたがっており、かつ異なったパートに分かれているOMakeのプロジェクト上では、まったく違うディレクトリ上でコマンドが実行されます。これはファイル、あるいはディレクトリの名前が位置的に独立して定義されている必要があることを表しています。 .. This is done with the $(file ) and $(dir ) functions. この問題は ``$(file )`` や ``$(dir )`` 関数を用いて解決できます。 :: osh> mkdir tmp osh> F = $(file fee) osh> section: cd tmp echo $F ../fee osh> echo $F fee .. Note the use of a section: to limit the scope of the cd command. The section temporarily changes to the tmp directory where the name of the file is ../fee. Once the section completes, we are still in the current directory, where the name of the file is fee. ``section:`` を用いて ``cd`` コマンドのスコープに制限を加えていることに注意してください。このセクションでは一時的に ``tmp`` ディレクトリに移動しているので、ファイルの名前には ``../fee`` が用いられます。このセクションが終了してカレントディレクトリに戻ってきたとき、ファイルの名前には ``fee`` が用いられます。 .. One common way to use the file functions is to define proper file names in your project OMakefile, so that references within the various parts of the project will refer to the same file. ファイル関数を使う主な目的は、あなたのプロジェクトの ``OMakefile`` で、ファイル名が正しく定義されるようにするためです。これを用いればプロジェクト上の様々なパートに移動したとしても、変数は同一のファイルを指し示します。 :: osh> cat OMakefile ROOT = $(dir .) TMP = $(dir tmp) BIN = $(dir bin) ... .. note:: 訳注: file, dirについて詳しく知りたい方は ":ref:`label10.1.1`" を参照してください。 .. index:: single: mapprefix() single: addprefix() single: addsuffix() single: mapsuffix() single: foreach() .. _label7.4: 7.4 イテレーション、マップ、foreach --------------------------------------- .. Most builtin functions operate transparently on arrays. ほとんどのビルドイン関数では配列を何も考慮することなく処理できます。 :: osh> addprefix(-D, DEBUG WIN32) - : -DDEBUG -DWIN32 : Array osh> mapprefix(-I, /etc /tmp) - : -I /etc -I /tmp : Array osh> uppercase(fee fi foo fum) - : FEE FI FOO FUM : Array .. The mapprefix and addprefix functions are slightly different (the addsuffix and mapsuffix functions are similar). The addprefix adds the prefex to each array element. The mapprefix doubles the length of the array, adding the prefix as a new array element before each of the original elements. ``mapprefix`` と ``addprefix`` 関数は全く異なります( ``addsuffix`` と ``mapsuffix`` 関数も同様です)。 ``addprefix`` 関数は接頭辞を各々の成分にくっつけます。 ``mapprefix`` 関数は元の成分の前に新しい接頭辞を成分として加えるので、配列の長さは2倍になります。 .. Even though most functions work on arrays, there are times when you will want to do it yourself. The foreach function is the way to go. The foreach function has two forms, but the form with a body is most useful. In this form, the function takes two arguments and a body. The second argument is an array, and the first is a variable. The body is evaluated once for each element of the array, where the variable is bound to the element. Let's define a function to add 1 to each element of an array of numbers. ほとんどの関数は配列でも動きますが、あなた自身が配列にも対応した関数を作りたいと思うこともあるでしょう。 ``foreach`` 関数はその要望を実現します。 ``foreach`` 関数は二つの表記を使いますが、このコードを伴った表記法はとても便利です。この関数は二つの引数とコードが必要です。まず、一つ目の引数は変数で、二つ目のは配列を指定します。そして ``foreach`` のコードは各々の成分を対象に、引数で指定された変数がその成分に束縛された状態で、配列の長さぶんだけ実行されます。さあ、それでは値を持った配列の各々の成分に1を加える関数を定義してみましょう。 :: osh> add1(l) = foreach(i, $l): add($i, 1) osh> add1(7 21 75) - : 8 22 76 : Array .. Sometimes you have an array of filenames, and you want to define a rule for each of them. Rules are not special, you can define them anywhere a statement is expected. Say we want to write a function that describes how to process each file, placing the result in the tmp/ directory. あなたはファイル名を持った配列を持ち、そのそれぞれにビルドルールを定義したいと思うこともあるでしょう。ビルドルールは特別なものではなく、あなたはどの箇所でもビルドルールを定義することができます。さて、私たちは配列にある、各々のファイルの処理について記述し、さらに結果を ``tmp/`` ディレクトリの中に置く関数を書きたいものとします。 :: TMP = $(dir tmp) my-special-rule(files) = foreach(name, $(files)) $(TMP)/$(name): $(name) process $< > $@ .. Later, in some other part of the project, we may decide that we want to use this function to process some files. 後に、プロジェクトの他の部分で、私たちはこの関数を用いていくつかのファイルを処理することを決めたとしましょう。 :: # src/libに処理するためのファイルが入っています MY_SPECIAL_FILES[] = fee.src fi.src file with spaces in its name.src my-special-rule($(MY_SPECIAL_FILES)) .. The result of calling my-special-rule is exactly the same as if we had written the following three rules explicitly. ``my-special-rule`` を呼んだ結果は、以下の3つのルールを明示的に書いた場合と全く同じになります。 :: $(TMP)/fee.src: fee.src process fee > $@ $(TMP)/fi.src: fi.src process fi.src > $@ $(TMP)/$"file with spaces in its name.src": $"file with spaces in its name.src" process $< > $@ .. Of course, writing these rules is not nearly as pleasant as calling the function. The usual properties of function abstraction give us the usual benefits. The code is less redundant, and there is a single location (the my-special-rule function) that defines the build rule. Later, if we want to modify/update the rule, we need do so in only one location. もちろん、これらのルールを記述することは関数を呼ぶことより好ましいものではありません。関数を抽象化することによる普通の特性は、普通の利点となります。ビルドルールを定義するためのコードが一つだけで済み、コードはさらに短くなります。後でもし私たちがルールを修正したりアップデートしたいと思ったときも、単純に一つのルールを修正するだけでよいのです。 .. index:: single: 遅延評価変数 .. _label7.5: 7.5 遅延評価式 --------------------------------------- .. Evaluation in omake is normally eager. That is, expressions are evaluated as soon as they are encountered by the evaluator. One effect of this is that the right-hand-side of a variable definition is expanded when the variable is defined. omakeでの評価は通常の場合先行して行われます。これは、omakeが式に遭遇した場合、即座に評価が行われることを意味しています。この効果の一つとして、変数定義式の右側は、変数が定義される時点で展開されることが挙げられます。 .. There are two ways to control this behavior. The $`(v) form introduces lazy behavior, and the $,(v) form restores eager behavior. Consider the following sequence. この振る舞いをコントロールするための2つの方法があります。 ``$`(v)`` は遅延評価を行うための、 ``$,(v)`` は先行評価に戻すための表記法です。以下のシーケンスについて考えてみましょう。 :: osh> A = 1 - : "1" : Sequence osh> B = 2 - : "2" : Sequence osh> C = $`(add $(A), $,(B)) - : $(apply add $(apply A) "2" : Sequence) osh> println(C = $(C)) C = 3 osh> A = 5 - : "5" : Sequence osh> B = 6 - : "6" : Sequence osh> println(C = $(C)) C = 7 .. The definition C = $`(add $(A), $,(B)) defines a lazy application. The add function is not applied in this case until its value is needed. Within this expression, the value $,(B) specifies that B is to be evaluated immediately, even though it is defined in a lazy expression. ``C = $`(add $(A), $,(B))`` では遅延評価を定義しています。 ``add`` 関数はこの場合、実際に値が必要となるときまで評価しません。上の式を見てみると、 ``$,(B)`` は ``B`` が即座に評価する変数であることを指定します。これが遅延評価式の中で定義されているにもかかわらずです。 .. The first time that we print the value of C, it evaluates to 3 since A is 1 and B is 2. The second time we evaluate C, it evaluates to 7 because A has been redefined to 5. The second definition of B has no effect, since it was evaluated at definition time. 最初私たちが ``C`` の値を出力したとき、 ``A`` は1で ``B`` が2であったので、結果は3と評価されました。次に私たちが同様に ``C`` を出力したとき、 ``A`` は5に再定義されていたので、結果は7と評価されました。二回目の ``B`` は ``C`` の定義時に評価されているため、なんの影響も与えていません。 .. _label7.5.1: 7.5.1 遅延評価式についての追加例 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. Lazy expressions are not evaluated until their result is needed. Some people, including this author, frown on overuse of lazy expressions, mainly because it is difficult to know when evaluation actually happens. However, there are cases where they pay off. 遅延評価式は実際に結果が必要とされるときまで評価されません。筆者を含む結構な人数のプログラマは、遅延評価を多用しているコードを見ると眉をひそめます。なぜならこれは実際にどこで評価が行われているのか分かりにくくなるからです。しかしながら、これらの難点をペイオフできるケースも確かに存在します。 .. One example comes from option processing. Consider the specification of “include” directories on the command line for a C compiler. If we want to include files from /home/jyh/include and ../foo, we specify it on the command line with the options -I/home/jyh/include -I../foo. 一つの例としてオプションの処理が挙げられます。Cコンパイラに"include"ディレクトリの指定をコマンドライン上から指定する場合を考えましょう。もし私たちが"/home/jyh/include"と"../foo"上のファイルをインクルードしたい場合、コマンドラインにはオプション ``-I/home/jyh/include -I../foo`` を指定する必要があります。 .. Suppose we want to define a generic rule for building C files. We could define a INCLUDES array to specify the directories to be included, and then define a generic implicit rule in our root OMakefile. Cファイルをビルドするための通常のルールを定義する場合について考えましょう。私たちはインクルードされるディレクトリを指定するため、 ``INCLUDES`` 配列を定義し、ルートの ``OMakefile`` に通常用いる暗黙のルールを定義しました。 :: # Cファイルをコンパイルする通常の設定 CFLAGS = -g INCLUDES[] = %.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $< # srcディレクトリは4つのソースファイルからmy_widgetをビルドします。 # これはインクルードディレクトリからインクルードファイルを読み込みます。 .SUBDIRS: src FILES = fee fi foo fum OFILES = $(addsuffix .o, $(FILES)) INCLUDES[] += -I../include my_widget: $(OFILES) $(CC) $(CFLAGS) -o $@ $(OFILES) .. But this is not quite right. The problem is that INCLUDES is an array of options, not directories. If we later wanted to recover the directories, we would have to strip the leading -I prefix, which is a hassle. Furthermore, we aren't using proper names for the directories. The solution here is to use a lazy expression. We'll define INCLUDES as a directory array, and a new variable PREFIXED_INCLUDES that adds the -I prefix. The PREFIXED_INCLUDES is computed lazily, ensuring that the value uses the most recent value of the INCLUDES variable. しかしこれは全く正しいというわけではありません。問題としては、 ``INCLUDES`` はオプションが格納されてある配列であり、ディレクトリではないという点です。もし私たちが後にディレクトリ名を変更したい場合は、まず ``-I`` 接頭辞を配列から分割する必要があり、これは混乱の元となります。さらに、私たちはディレクトリの絶対パスを使用していません。この問題を解決する方法は、遅延評価式を使うことです。まず私たちは ``INCLUDES`` をディレクトリの配列として定義し、さらに新しい変数 ``PREFIXED_INCLUDES`` を定義することによって ``-I`` 接頭辞を追加します。 ``PREFIXED_INCLUDES`` は遅延評価を行うことで、最新の ``INCLUDES`` 変数値が使われることを保証してくれます。 :: # Cファイルをコンパイルする通常の設定 CFLAGS = -g INCLUDES[] = PREFIXED_INCLUDES[] = $`(addprefix -I, $(INCLUDES)) %.o: %.c $(CC) $(CFLAGS) $(PREFIXED_INCLUDES) -c $< # 今回の例では、私たちはインクルードディレクトリの絶対パスを定義しました。 STDINCLUDE = $(dir include) # srcディレクトリは4つのソースファイルからmy_widgetをビルドします。 # これはインクルードディレクトリからインクルードファイルを読み込みます。 .SUBDIRS: src FILES = fee fi foo fum OFILES = $(addsuffix .o, $(FILES)) INCLUDES[] += $(STDINCLUDE) my_widget: $(OFILES) $(CC) $(CFLAGS) -o $@ $(OFILES) .. Note that there is a close connection between lazy values and functions. In the example above, we could equivalently define PREFIXED_INCLUDES as a function with zero arguments. 遅延する値と関数が密接に繋がっていることに注目してください。上の例では、私たちは ``PREFIXED_INCLUDES`` を、引数を持たない関数として定義しているのと同じことを行っています。 :: PREFIXED_INCLUDES() = addprefix(-I, $(INCLUDES)) .. index:: single: while .. _label7.6: 7.6 スコープとエクスポート --------------------------------------- .. The OMake language is functional (apart from IO and shell commands). This comes in two parts: functions are first-class, and variables are immutable (there is no assignment operator). The latter property may seem strange to users used to GNU make, but it is actually a central point of OMake. Since variables can't be modified, it is impossible (or at least hard) for one part of the project to interfere with another. OMakeの言語は(IOとシェルコマンドは除きますが)関数型言語となっています。これは、まず関数が最上級のものであることと、変数が不変なものである(代入という操作が存在しない)という2つから、そうであると言えるでしょう。後者に関しては、おそらく従来のGNU makeを使っていたユーザからすると奇妙なものに思えるかもしれません。しかしこれは実際にOMakeを使う上で非常に重要な点となります。変数は修正できませんので、プロジェクトの一部分が他の部分に干渉することは不可能(あるいは非常に困難)です。 .. To be sure, pure functional programming can be awkward. In OMake, each new indentation level introduces a new scope, and new definitions in that scope are lost when the scope ends. If OMake were overly strict about scoping, we would wind up with a lot of convoluted code. これを従来の純粋な関数型言語のように実装してしまうと、非常に使いにくいものになるかもしれません。OMakeでは、インデントすることでレベルを一つ挙げた場合、新しいスコープが導入されます。そしてスコープが終わると、そのスコープで新しく定義された変数は消去されます。もしOMakeが馬鹿真面目にスコープに関して厳格な仕様であったなら、おそらくコードはもっと複雑なものになったでしょう。 :: osh> X = 1 osh> setenv(BOO, 12) osh> if $(equal $(OSTYPE), Win32) setenv(BOO, 17) X = 2 osh> println($X $(getenv BOO)) 1 12 .. The export command presents a way out. It takes care of “exporting” a value (or the entire variable environment) from an inner scope to an outer one. ``export`` コマンドはこの制限を外に出します。このコマンドは内部のスコープの値(あるいは全体の変数環境)を外部に『エクスポート』するお世話をします。 :: osh> X = 1 osh> setenv(BOO, 12) osh> if $(equal $(OSTYPE), Win32) setenv(BOO, 17) X = 2 export osh> println($X $(getenv BOO)) 2 17 .. Exports are especially useful in loop to export values from one iteration of a loop to the next. エクスポートは、ループ中のイテレーションから次のイテレーションへ値をエクスポートするのに特に役立ちます。 :: # オーケー、それでは配列の各成分を足し合わせてみよう osh>sum(l) = total = 0 foreach(i, $l) total = $(add $(total), $i) value $(total) osh>sum(1 2 3) - : 0 : Int # おっと、正常に動いていないじゃないか! osh>sum(l) = total = 0 foreach(i, $l) total = $(add $(total), $i) export value $(total) osh>sum(1 2 3) - : 6 : Int .. A while loop is another form of loop, with an auto-export. ``while`` ループは自動的にエクスポートしてくれる、別の形のループ文です。 :: osh>i = 0 osh>total = 0 osh>while $(lt $i, 10) total = $(add $(total), $i) i = $(add $i, 1) osh>println($(total)) 45 .. index:: single: シェルエイリアス single: Shell .. _label7.7: 7.7 シェルエイリアス --------------------------------------- .. Sometimes you may want to define an alias, an OMake command that masquerades as a real shell command. You can do this by adding your function as a method to the Shell object. ときどきあなたは *エイリアス* を定義したいと思うことがあるかもしれません。そのためにOMakeでは、実際にシェルコマンドが存在しているかのようにふるまうコマンドが存在します。あなたはこれを、 ``Shell`` オブジェクトに対象の関数を追加することで実現できます。 .. For an example, suppose we use the awk function to print out all the comments in a file. 例えば、 ``awk`` 関数を用いて、あるファイル中のすべてのコメントを出力する場合について考えてみましょう。 :: osh>cat comment.om # Comment function comments(filename) = awk($(filename)) case $'^#' println($0) # File finished osh>include comment osh>comments(comment.om) # Comment function # File finished .. To add it as an alias, add the method (using += to preserve the existing entries in the Shell). これをエイリアスとして追加するには、 ``Shell`` オブジェクトにメソッドを追加します。 ``+=`` を用いて、シェルの既存の内容を保存している点に注意してください。 :: osh>Shell. += printcom(argv) = comments($(nth 0, $(argv))) osh>printcom comment.om > output.txt osh>cat output.txt # Comment function # File finished .. A shell command is passed an array of arguments argv. This does not include the name of the alias. シェルコマンドは引数として配列 ``argv`` が渡されます。これはエイリアスの名前には *含まれていません* 。 .. index:: single: リダイレクション single: stdin single: stdout single: stderr .. _label7.8: 7.8 簡単に入出力のリダイレクションを行う ----------------------------------------- .. As it turns out, scoping also provides a nice alternate way to perform redirection. Suppose you have already written a lot of code that prints to the standard output channel, but now you decide you want to redirect it. One way to do it is using the technique in the previous example: define your function as an alias, and then use shell redirection to place the output where you want. 結果的に、スコーピングによってリダイレクションの実行に関しての良い代替案も表れることとなりました。それでは、既に標準の出力先に出力するコードが大量にあるが、この出力先のリダイレクションを行いたいというような場合について考えてみましょう。まず一つ目の方法としては、前回のテクニックを用いることが挙げられます。具体的には、関数をエイリアスとして定義し、あなたが望む出力先にすることでシェルのリダイレクションを行うといった方法です。 .. There is an alternate method that is easier in some cases. The variables stdin, stdout, and stderr define the standard I/O channels. To redirect output, redefine these variables as you see fit. Of course, you would normally do this in a nested scope, so that the outer channels are not affected. 別の方法については、前者の方法よりも簡単な場合があります。変数 ``stdin`` ``stdout`` ``stderr`` は標準I/Oの出力先について定義しています。出力先をリダイレクトするには、これらの変数をあなたが望むように再定義します。もちろん、あなたはこれを普通にネストされたスコープ上で行うことができるので、外部の出力先に影響を与えることはありません。 :: osh>f() = println(Hello world) osh>f() Hello world osh>section: stdout = $(fopen output.txt, w) f() close($(stdout)) osh>cat output.txt Hello world .. This also works for shell commands. If you like to gamble, you can try the following example. これはシェルコマンドに対しても同様です。もしあなたがギャンブル好きであるならば、以下の例を試してみるのもいいでしょう。 :: osh>f() = println(Hello world) osh>f() Hello world osh>section: stdout = $(fopen output.txt, w) f() cat output.txt close($(stdout)) osh>cat output.txt Hello world Hello world