このテクニカルノートでは、do shell scriptの使い方に関するよくある質問を取り上げます。Unixのシェルスクリプトで実行できることや書き方については説明しません。それについては、適切なUnixの参考資料を見つけるか、身近にいるエキスパートに相談してください。本稿はQ&A形式で構成されているため、関心のある問題にすぐにスキップすることも、始めから終わりまで通読することもできます。 いくつかの回答で「manページ」に触れていますが、これらはMac OS Xに含まれている参考ドキュメントです(「man」は「manual」の省略)。あるコマンドのmanページを参照するには、「ターミナル」ウインドウを開き、 コマンドの発行Q:シェルコマンドにAppleScript変数を渡すには、どうしたらよいのでしょうか?A:do shell scriptへのコマンド引数は実際には単なる文字列であるため、AppleScript連結演算子 set hostname to "www.apple.com" do shell script "ping -c1 " & hostname コマンドによっては、データを標準入力に渡す必要があります。do shell scriptはこれを直接的にはサポートしていませんが、 set input to "hello" do shell script "echo " & input & " | tr a-z A-Z" -- "HELLO" 一般には、 Q:「ターミナル」では正常に動作するコマンドが、do shell scriptで使おうとすると、「command not found(コマンドが見つかりません)」というエラーが表示されます。どうなっているのでしょうか?A:2つの可能性があります。1つは、do shell scriptは常に/bin/shを使用してコマンドを解釈し、「ターミナル」が使用するデフォルトのシェルを使用していることです(デフォルトのシェルを見つけるには、「ターミナル」で「 もう1つは、コマンド名だけを使用した場合、シェルは(PATHとして知られる)ディレクトリのリストを使って、そのコマンドの完全パスを探そうとします。セキュリティと移植性の理由から、do shell scriptは、対話型シェルが読み取るような構成ファイルを無視するため、「ターミナル」でカスタマイズが行われていたとしてもそれは利用されません。このため、単に「 (この回答ではいくつかの詳細に触れていません。関心のある方は「細かい話」を参照してください)。 Q:do shell scriptは、なぜ「ターミナル」とまったく同じように動作しないのですか?A:2つの理由があります。1つは、スクリプトを、異なるシステムで変更することなく実行できるよう保証するためです。あるユーザのデフォルトシェルやPATHを使ったdo shell scriptは、他のユーザの環境では、おそらく実行が中断してしまいます。もう1つは、do shell scriptはPerlなどの他の言語のシェルエスケープのメカニズムに合わせているためです。 Q:sh以外のシェルでコマンドを実行するには、どうしたらよいのでしょうか?A:使用するシェルを、コマンドで明示的に指定します。選択したシェルにコマンドを渡す方法は、いくつかあります。コマンドをファイルに書き、次のようにファイルを実行する方法もあります。 do shell script "/bin/tcsh my-command-file-path" いくつかのシェルは、次のようにパラメータとしてスクリプトを受け付けます do shell script "/bin/tcsh -c 'my-command'" また、大部分のシェルは、次のように標準入力からスクリプトを受け付けます。 do shell script "echo my-command | /bin/tcsh" 不明な点については、使用するシェルのドキュメントを参照してください。do shell script文字列の中でコマンドを使う場合は、コマンドを引用符で囲む必要があります。そうしないと、shはコマンドの特殊文字を解釈します。 Q:1つのdo shell scriptで複数のコマンドを使用するには、どうしたらよいのでしょうか?たとえば、cdコマンドであるディレクトリに移動してから何らかの操作を行いたいのですが、あるコマンドから別のコマンドに移るときに、作業ディレクトリが記憶されません。A:do shell scriptを呼び出すたびに、新しいシェルプロセスが使用されるため、変数や作業ディレクトリの変更などの状態は、ある呼び出しから次の呼び出しに移る際に保存されません。1回の呼び出しで複数のコマンドを実行するには、次のようにセミコロンでコマンドを区切ります。 do shell script "cd ~/Documents; ls" -- result:"Welcome.txt" 改行(ASCII文字10)を使用しても正しく機能します。 Q:あるコマンドに関する管理者特権を取得するにはどのようにすればよいですか?A:次のように do shell script "command" user name "me" password "mypassword" with administrator privileges
セキュリティ上の理由で、tellで別のアプリケーションを管理者権限のあるdo shell scriptに指定することはできません。コマンドはtellブロックの外側に置くか、tell meブロックの内側に置いてください。 管理者権限があれば、システム中のすべての場所にあるすべてのファイルを変更できることに注意してください。的確なコマンドをいくつか使用するだけで、システムを起動できない状態にしたり、ディスク全体を消去することさえ可能になるため、用心する必要があります。できれば、どうしても必要な場合以外は、管理者権限は使用しないことをお勧めします。システムレベルでの開発を行っている場合を除き、/System内を変更する必要はまずありません。通常は/Libraryの変更で十分です。 注:sudo(8)と 警告:Mac OS X 10.4.0および10.4.1では、 do shell script "/usr/bin/perl -Ue '$< = $>; system(@ARGV)' my_command" with administrator privileges Mac OS X 10.4.2では実ユーザIDと実効ユーザIDを設定するため、ここで述べた回避方法は不要ですが、害はありません。 警告:Mac OS X 10.4より前には、 set normal_command to "command1; command2" do shell script "sh -c " & quoted form of normal_command with administrator privileges Mac OS X 10.4現在、前述の「複数のコマンドを使用するには、どうしたらよいのでしょうか?」で述べたように、 Q:コマンドはどのくらい長くできますか?最大文字数はどれくらいですか?A:この質問に対する正確な回答はありません(理由については、「細かい話」を参照)。ただし、概算の回答として、最大約262,000文字までを1つのコマンドとして作成できます。技術的にいえば、1文字あたり1バイトと仮定すると、262,000バイトまでです。非ASCII文字は1文字あたり少なくとも2バイトを使用します。詳細については、「テキストの扱い」を参照してください。 注:以前、この制限はもっと小さく、Mac OS X 10.2では約65,000バイトでした。シェルコマンド この制限を超えると、do shell scriptがタイプ255のエラーを返します。この制限を超えるのは、コマンドにインラインデータを渡そとしているケースがほとんどです。インラインデータを渡すのではなく、ファイルにデータを書き込み、ファイルから読み取ることを検討してください。 結果の取得Q:do shell scriptはどのようにして結果を取得するのですか?シェル変数をAppleScriptに返すには、どうしたらよいのでしょうか?A:シェルコマンドは、標準出力と標準エラー出力という2つの出力ストリームの一方に結果を書き出せます。標準出力は通常の出力に使われ、標準エラー出力はエラーメッセージと診断に使われます。スクリプトが正常に完了したと仮定すると(正常でない場合は次のQ&Aを参照)、結果は標準出力に出力されたテキストに、おそらくいくつかの修正が加えられたものになります。ほとんどのコマンドは結果を標準出力に自動的に出力するため、余計なことは何もする必要はありません。結果が変数にある場合は、 リスト1:AppleScript変数のmySlugを日付スラグにyyyy-mm-ddフォーマットで設定します。 set mySlug to do shell script "date +%Y-%m-%d" -- see the 'date' man page for details on the format string. リスト2:同じことを実行するPerlスクリプト。 set mySlug to do shell script ¬
"perl -e 'my (undef, undef, undef, $d, $m, $y) = localtime;
my $date = sprintf("%4d-%02d-%02d", $y+1900, $m+1, $d);
print $date'"デフォルトでは、do shell scriptは結果内の行末をすべてMac形式の復帰改行(\rまたはコード番号13のASCII文字)に変換し、(もしあれば)行末の終了文字を1つ除去します。つまり、「 Q:do shell scriptはエラーをどのように報告するのですか?A:シェルコマンドはすべて、完了時に整数のステータスを返します。0は成功を表し、それ以外は失敗を表します。0以外のステータスを持つスクリプトがある場合、do shell scriptはステータスがエラー番号であるAppleScriptエラーをスローします(コマンドのmanページに、そのコマンドがどのステータスコードを返せるかが記載されているはずです。大部分のコマンドは、すべてのエラーに単に1を使用します)。スクリプトが標準エラーストリームに対して何かを出力した場合は、そのテキストがAppleScriptのエラーメッセージになります。エラーテキストがない場合、(もしあれば)通常の出力がエラーメッセージとして使用されます。 Q:「ターミナル」でコマンドを実行すると一連の出力が表示されるのですが、do shell script を使用すると、その一部は表示されません。A:「ターミナル」でコマンドを実行する場合、標準出力と標準エラー出力は両方とも同じ場所に送信されるため、それらを見分けることは困難です。これに対して、do shell scriptは2つのストリームを分離した状態で維持します。標準出力と標準エラー出力を結合したい場合は、次のようにコマンドの後に do shell script "command 2>&1" 詳細については、manページの「Redirections」を参照してください。 Q:コマンドでどのくらいの出力を返せますか?A:単一のコマンドで、最大1GBのデータを返すことができます。 テキストの扱いQ:パラメータにスペースや区切り文字(括弧、$、*など)が含まれていると、コマンドが正しく動作しません。A:シェルは複数のパラメータをスペースで区切り、いくつかの区切り記号には特殊な意味があるため、スペースや区切り記号などを含む文字列を1つのパラメータとしてシェルで処理するには、特別な手順が必要になります。この手順は「クォーティング」と呼ばれ、いくつかの方法がありますが、最も簡単で効果的なのは、文字列の たとえば、次の(不完全な)ハンドラについて考えてみます。このハンドラは文字列を取得して、ホームディレクトリにある「stuff」というファイルに追加します。 to append_message(s)
do shell script "echo " & s & " >> ~/stuff"
end append_message大部分の文字列に対してはうまくいきますが、「$100」などの文字列を使って呼び出すと、この文字列がファイルに追加されるときには、「00」となってしまいます。これは、シェルが“$1”を、値が空の文字列である変数と解釈するためです(shの変数は、先頭にドル記号が付きます)。このスクリプトを修正するには、次のように変更します。 do shell script "echo " & quoted form of s & " >> ~/stuff"
Q:シェルコマンドで二重引用符とバックスラッシュを使う必要があるのですが、AppleScriptで実行すると構文エラーになります。A:AppleScriptでは、文字列は二重引用符で始まり二重引用符で終わります。文字列の中でリテラルとしての二重引用符を使うには、次のようにバックスラッシュ文字を使って、「エスケープ処理」を行う必要があります。 "a \"quote\" mark" バックスラッシュには、「次の文字を特別に扱う」という意味があります。このため、リテラルのバックスラッシュを使用するには、次のように2つのバックスラッシュが必要になります。 "a back\\slash" 以上をまとめると、次のようするとうまくいくはずです。 set s to "this is a test." do shell script "echo " & quoted form of s & " | perl -n -e 'print \"\\U$_\"'" -- result: "THIS IS A TEST." スクリプトにはバックスラッシュが追加されていますが、Perlの-eオプションに渡される実際の文字列は、次のようになります。 print "\U$_" Q:シェルスクリプトが二重引用符やバックスラッシュを返すときには常に、その先頭に余分なバックスラッシュが付いています。A:結果ウインドウでは、スクリプトにペーストしてそのままコンパイルできるように、結果が「ソース」形式で表示されます。つまり、文字列の結果は引用符で囲まれ、二重引用符やバックスラッシュなどの特殊文字は前述のようにエスケープ処理されるということです。追加されたバックスラッシュは、実際には文字列の一部ではなく、単に表示されているだけです。その文字列を Q:do shell scriptは、非ASCIIテキスト(アクセント記号付き文字や日本語など)をどのように扱いますか?A:AppleScriptの観点からすると、do shell scriptはUnicodeテキストを受け付け、生成します。do shell scriptはUTF-8を使用して、コマンドをシェルに渡し、出力を解釈します。コマンドが有効なUTF-8でなバイトを生成すると、do shell scriptはシステムのプライマリエンコードを使って解釈します。 大部分のシェルコマンドは、UnicodeとUTF-8を完全に無視することを考慮してください。ASCII文字に関しては、UTF-8とASCIIは類似しています。たとえば、「 注:AppleScript 1.8.3より前には、do shell scriptはプライマリシステムエンコーディングを使用して入出力を解釈していたので、名前に非ASCII文字のあるファイルを処理するのはきわめて困難でした。 Mac OS X 10.4より前では、有効なUTF-8でない出力は「can’t make some data into the expected type(いくつかのデータを期待されている種類に変換できない)」エラーを生成します。解決策としては、出力をファイルに書き出し、AppleScriptのreadコマンドを使うか、visを通じてパイプ処理を行って読み取る方法があります。10.4以降では、有効なUTF-8でない出力は、プライマリシステムエンコーディングを使用して解釈されます。 Q:行末に関してはどのような規則がありますか?A:Mac OS Xでは、行末には2種類の規則があります。Mac形式(行は復帰、すなわち\rまたはコード番号13のASCII文字で終了)と、UNIX形式(行は改行、すなわち\nまたはコード番号10のASCII文字で終了)です。シェルコマンドは通常、UNIX形式の行末のみを処理するため、Mac形式のテキストを渡すと、そのままでは使用できない結果が得られます。たとえば、grepでは入力全体には1行しかないとみなされるため、一致は多くても1つになります。 データがAppleScriptのものであれば、行末を変換するか、先頭位置に改行を生成することができます。改行は、 set f to choose file do shell script "tr '\\r' '\\n' > " & quoted form of POSIX path of f & " | grep something" AppleScript自体は行末の種類を区別しません。stringオブジェクトとUnicode textオブジェクトの ファイルの扱いQ:AppleScriptのfileオブジェクトまたはaliasオブジェクトを取得した後、これをシェルコマンドに渡すには、どのようにすればよいですか?A:シェルは、POSIXパス名、すなわちパスの要素がスラッシュで区切られた文字列を使ってファイルを指定します(たとえば、 POSIX path of file "HD:Users:me:Documents:Welcome.txt" -- result:"/Users/me/Documents/Welcome.txt" たとえば、シェルコマンドが結果としてPOSIXパスを返す場合は、 set p to do shell script "echo ~" POSIX file p -- result:file "HD:Users:me:" Q:ファイル名に、スペース、括弧、$、*などの文字が含まれていると、POSIX pathが正しく動作しません。A:これはクォーティングの特殊ケースです。シェルがすべての区切り文字をリテラルとして解釈できるようにするには、パスを引用符で囲む必要があります。これを行うには、パスのquoted formを使います。次の例は、名前に関係なく、どのようなファイルにも正しく動作します。 choose file do shell script "ls -l " & quoted form of the POSIX path of the result -- result:"-rw-r--r-- 1 me unknown 1 Oct 25 17:48 Look! a file!" Q:POSIX pathがすべてを引用符で囲まないのはなぜですか?A:2つの理由があります。第1に、POSIX pathにはシェルパラメータとまったく関係のない使用法があり、そのようなケースではパスを引用符で囲むことは適切ではありません。第2に、quoted formはファイルパス以外のものに対しても効果があります。このため、POSIX pathはすべてを引用符で囲みません。 その他の問題点Q:ftpやtelnetなどの対話型ツールをdo shell scriptで制御するには、どのようにすればよいのですか?A:簡単にいうと、方法はありません。do shell scriptはコマンドを開始し、コマンドが完了するまでは対話なしで実行するように設計されています。多くのUNIXシェルやPerlのバッククォート演算子によく似ています。 ただし回避策はあります。「ターミナル」のスクリプトを作成して、同じウインドウへ一連のコマンドを送信する方法(ただし、Mac OS X 10.2以降のみ)や、対話型ツールのスクリプティング用のUNIXパッケージ(expectなど)を使う方法があります。また、多くの対話型コマンドには、非対話型の同等のコマンドがあります。たとえば、多くの場合、 Q:スクリプトの出力に非常に時間がかかります。結果が出た時点でそれを読むには、どうすればよいですか?A:これについても方法はありません。do shell scriptはコマンドが完了するまで戻りません。UNIXの言葉でいえば、do shell scriptを使ってパイプを作成することはできません。ただし対処法として、コマンドをバックグラウンドで実行し(次のQ&Aを参照)、出力をファイルに送信して、ファイルが完成したらファイルを読むという方法があります。 Q:バックグラウンドサーバプロセスを開始したい場合、do shell scriptにコマンドの完了を待機させないようにするには、どうすればよいですか?A: 注: do shell script "command > /dev/null 2> file_path 詳細については、shのmanページで「Redirection」を参照してください。 Q:バックグラウンドプロセスを開始した後、他のシェルコマンドで制御できるようにプロセスIDを取得するには、どうしたらよいのでしょうか?A:これを行うには、shの機能を利用できます。特別な変数の do shell script "my_command &> /dev/null & echo $!" -- result: 621 set pid to the result do shell script "renice +20 -p " & pid -- change my_command's scheduling priority. do shell script "kill " & pid -- my_command is terminated. Q:topを使おうとすると、「can't get terminal attributes(ターミナル属性を取得できません)」または「error opening terminal: unknown(「ターミナルを開けません:不明」)」というエラーになります。A:topは、デフォルトのモードではダイナミックに更新される表示画面を作成するために、さまざまな賢い動作をしますが、do shell scriptがカーソルコントロールをサポートしていないのと同じように、出力デバイスがカーソルコントロールをサポートしてない場合は、こうした動作はうまく機能しません。しかし、topにはロギングモードで動作するオプションがあり、do shell scriptのようなファイルに似たデバイスに対しては正常に動作します。代わりに この問題は、ターミナルがあることを想定している他のすべてのコマンドにも当てはまります。幸い、その大部分はターミナルを前提としていない比較的単純なコマンドへの、対話型のフロントエンドです。 Q:do shell scriptコマンドのデフォルトの作業ディレクトリはどこですか?A:do shell scriptは親プロセスの作業ディレクトリを継承します。「スクリプトエディタ」など、ほとんどのアプリケーションでは、デフォルト作業ディレクトリは「/」です。osascriptでは、osascriptを起動した時点におけるシェルの作業ディレクトリになります。特定のディレクトリが必要である場合は、デフォルトの作業ディレクトリを使用しないでください。」作業ディレクトリを特定の場所にする必要がある場合は、自分自身で設定する必要があります。 Q:do shell scriptにどのアプリケーションをtellで指定するかで違いはあるのでしょうか?A:最も予測可能な結果を得るには、do shell script呼び出しを必ずtellブロックの外側に置くか、 問題になるのは、どのアプリケーションに指示するかによって、シェルスクリプトの環境(作業ディレクトリ、環境変数など)が決まることです。ほとんどのアプリケーションは同じ環境ですが、それを当てにするとメンテナンスリスクを負うことになります。osascriptでAppleScriptを実行すると、osascriptを実行したシェルから作業環境が引き継がれますが、それは他のアプリケーションから完全に分離されます。「ターミナル」でshを実行する場合は、次のやり取りを考えてみてください。 $ VAR=something; export VAR $ osascript -e 'do shell script "echo $VAR"' something $ osascript -e 'tell application "Finder" to do shell script "echo $VAR"' (nothing) これがどのように機能するかについては、「細かい話」を参照してください。 細かい話このセクションは、シェルスクリプトが機能する仕組みを詳細に知りたい方を対象にしています。スクリプトを正しく機能させたいだけなら、このセクションを読む必要はありません。 Q:実際、do shell scriptはどのシェルを使用するのですか?A:do shell scriptは必ず/bin/shを呼び出します。しかし、Mac OS Xでは、/bin/shは実際にはshをエミュレートする別のシェルのコピーです。10.2以降では、それはbashであり、それより前はzshでした。 Q:実際、コマンドはどのくらい長くできますか?A:do shell scriptを呼び出すと新しいshプロセスが作成されるため、新しいプロセスにデータを渡す場合に関するシステムの通常の制限に制約されます。引数(この場合は、コマンドのテキストに加えて約40バイトのオーバーヘッド)と環境変数は、現在262,144バイトであるkern.argmaxより大きくできません。do shell scriptは親の環境を継承するため(次のQ&Aを参照)、コマンドテキストに利用できる正確な容量は呼び出し側の環境によって決まります。実際の問題として、その容量は261,000バイトより若干大きくなりますが、通常の環境設定では大幅に削減される可能性もあります。 Q:シェル環境(環境変数、作業ディレクトリなど)はどこから継承されるのですか?A:do shell scriptは親プロセスの環境を継承します。do shell scriptがスクリプティング機能追加のように機能するため、do shell script呼び出しの周りにあるtellブロックのアプリケーションが親プロセスです。tellブロックの周りに何もなかったり、tell meを使用している場合は、スクリプトを実行するアプリケーションが親です。 環境は作業ディレクトリ、環境変数、その他の属性に及びます。完全なリストについては、execve(2)のmanページを参照してください。「コマンドの発行」で述べたように、do shell scriptは、「ターミナル」で実行されている対話型シェルが読み取るような構成ファイルを読み取りません。 Finderから起動したアプリケーションは、同じデフォルト環境(作業ディレクトリおよび環境変数HOME、LANG、PATH、USER、SHELL)を取得します(必要であれば、さらに環境変数を定義できます。詳細については、テクニカルQ&A「Setting environment variables for user processes」を参照)。ほとんどのアプリケーションはその環境を変更しませんが、このことに依存するのはメンテナンスのリスクとなります。 osascript(1)は実行されたシェルの環境を継承します。作業ディレクトリはシェルの作業ディレクトリです。シェルで定義された環境変数が、osascript、ひいてはdo shell scriptでも定義されます。たとえば、次のようになります。 $ VAR=something; export VAR $ osascript -e 'do shell script "echo $VAR"' something shでは独自の環境変数をいくつか定義しますが、それが実行されても同じように継承されます。 ドキュメント改訂履歴
掲載日: 2006-03-23 | ||||||||||||||||
|