• ベストアンサー

実行中のシェルスクリプト自体を修正した場合の動作

シェルスクリプトで自分自身を削除するような構文を組んだとしても最後まで実行されました。 しかし、自分自身に別の同名ファイルを上書きコピーした場合は、 中途半端にコピー後のシェルが実行されました。 お聞きしたいのは、  ・「削除した場合でもスクリプトは最後まで実行される」という認識は正しいのか  ・コピーした場合はどうなるのか の2点です。 今回試したところ、コピーした場合は2行目の途中からコピー後のスクリプトが実行されたのですが、 これはマシンのスペックによるのでしょうか? 「コピーした場合はどうなるのかわからない」と判断した方がよいでしょうか? 現在、自分自身を修正するようなスクリプトを作成したいのですが、 上記認識で作ってよいのかどうか迷っています。 よろしくお願いします。 ▼以下、今回試した内容です。 goというスクリプトを作成しました。  #!/bin/bash  rm -rf ./go  echo "1"  echo "2"  echo "3"  echo "4"  echo "5" のように書かれています。 これをカレントディレクトリで ./go のように実行します。 実行結果は特にエラーメッセージも出ず、 goスクリプトは消え、1~5がコンソールに表示されました。 ファイル自体を削除されてもスクリプトを終えるまではコマンドは生きているように見えます。 上記goファイルのrm -rf ./goを、  cp -rf ../go . に修正し、 上の階層に同名のgoスクリプトを用意します。 こちらのgoには  #!/bin/bash  cp -rf ../go .  echo "A1"  echo "A2"  echo "A3"  echo "A4"  echo "A5" と書いています。 この状態で、最初の階層でgoを実行します。 最初の実験結果から、  ・カレントのgoの内容は上の階層のgoをコピーされたもの(echo "A1"~の方)になる。  ・コンソールには1~5が表示される と考えました。 実際は、当然goファイルは上の階層のgoをコピーしたものになりましたが、 コンソールには、 ./go: A1: command not found A2 A3 A4 A5 と表示されました。 2行目の途中から、上の階層のgoの内容が実行されているように見えます。 また2回目の環境でコードを、  #!/bin/bash  rm -rf ./go  cp -rf ../go .  echo "1"  echo "2"  echo "3"  echo "4"  echo "5" にした場合は、コンソールに表示されたのはA1~A5ではなく1~5でした。 ▲上記が試した内容です。

質問者が選んだベストアンサー

  • ベストアンサー
  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.3

まずunix/linuxのファイルシステムの基本として、rm しただけではファイルは消えません。オープン中のファイルをrmした場合、そのファイルをオープンしているプロセスがすべて終了した段階で削除されます。それまでは元のファイルが読み書きされます。 シェルは、一文ずつファイルから読み込んで実行します。実行中にファイルが上書きされることは想定していないので、cp で上書きした場合は、cp文の末尾のバイト位置の次から読み続けられます。従って、新しいファイルの途中から続行されることになります。そのバイト位置が新しいファイルで文の途中であれば、おそらくエラーになりますね。試された状況では、ファイルの前半が同じなのでちょうどうまく続行できたわけです。 rm して cp した場合は上書きではありませんので、最初に書いたとおり、シェルはrmした古いファイルを読み続けることになります。

tktk1228
質問者

お礼

回答ありがとうございます。 よくわかりました。 rmでいきなり消えるわけじゃないんですね。 LinuxはWindowsと違って消したらアウトみたいなイメージがありましたけど。 少し前に作ったスクリプトは、テストして巧く動いたので使い始めましたが、 テスト中は「ちょうどうまく続行できた」だけであることがわかりました。 あああ早く修正してこないと…。

その他の回答 (2)

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.2

>bashが何をしているのかについては何を見たらよいのかわからないのですが GNU-BashはGNU精神に基づきソースコードが公開されている(バージョンによってはソースコードを自前でコンパイルしてインストールする)ので、GNUソースコードライブラリを参照し、ソースコードを読んで下さい。 ソースコードを読めば、bashが何をしているか判ります。

tktk1228
質問者

お礼

回答ありがとうございます。 ソースコードを見ても理解できそうにありませんが、 探してみます。

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.1

「OSのバージョンと、bashの実装に依存する」が答え。 もしbashが「スクリプトをオープンしたまま」であれば「オープン中のファイルを削除しようとした場合にrmはどういう挙動にでるか?」って事になる。 もしbashが「スクリプトを1行実行する度に、fgetsで1行づつ読み込みしている」であれば「オープン中のファイルに上書きした場合にfgetsはどういう挙動にでるか?」って事になる。 もしbashが「スクリプトを最初に全部読み込んでしまう」であれば「実行開始したら自己の変更は不可能」って事になる。 で、OSが変われば(FreeBSDとかSoralisとかLinuxとか)bashの実装方法も変わるし、rmやcpやfgetsの実装方法も異なる。 つまり「今の、その環境ででは、そう動く。環境が変わったら同じ動きをする保証はどこにもない」と言う事。 環境によっては「core吐いて止まる」かも知れませんよ。

tktk1228
質問者

お礼

回答ありがとうございます。 よくわかったような気がします。 bashが何をしているのかについては何を見たらよいのかわからないのですが、 (試してみるしかないわけじゃないと思いますが) このディストリビューションで同じようにインストールしたもの(デフォルト状態)であれば同じ動きをする、 ということはわかったので問題なさそうです。 使おうとしている環境は同じですので。 挙げた例の、  rm -rf ./go  cp -rf ../go . を使って修正(更新)してみようと思います。 ありがとうございました。

関連するQ&A

  • シェルスクリプトからPHP実行

    シェルスクリプト(bash)内で、以下のようにPHPを呼んだ場合、 その次の処理がPHPの終了を待たずに実行されるようなのですが、 どうしてでしょうか? #!\bin\bash /usr/local/php4/bin/php /home/test.php echo "test!" 以上 なぜか test! という表示がPHPの処理中に出るように思えます。

  • シェルスクリプトで$RANDOMの値が表示できません

    #!/bin/sh DATA=$RANDOM echo $DATA としたとき,何も出力されないでスクリプトが終了してしまいます.bashモードでコンソールから echo $RANDOM とやれば乱数は表示されます. どのようにすれば表示させることができるのでしょうか?

  • シェルスクリプトの作り方

    OSはVineで、bashシェルです。 シェルスクリプトの作り方ですが、 1.ファイルを作る。 例えば、 #!bin/sh cd tmp このとき、ディレクトリはどこでもいいのでしょうか? 2.権限を付ける。 chmod u+x filename 3.実行 ./filename 他に気をつけることはないでしょうか? 実行すると、 bash: ./filename: bad interpreter: そのようなファイルやディレクトリはありません となってしまって、どうしても実行できません。

  • シェルスクリプトの1行目に#!を書いてもダメなのはなぜ?

    test.shというシェルスクリプトを #!/bin/bash echo "bash test" のように書き、chmod +x test.shを実行し、 test.shと入力すると、bash testが表示されるはずですが、 エラーbash: test.sh :command not found が出ます。which bashでパスを見ると /bin/bashなので、問題ないはずなんですが。 ちなみに、bash test.shと入力すると、正常に実行されます。 同様のことがperlスクリプト #!/usr/bin/perl print "perl test\n"; についても起こります。 何か設定がまずいのでしょうか? RedHatLinux7.2を使用しています。

  • シェルスクリプトの記載方法

    linuxでシェルスクリプトを書いています サーバ上に「日付のついた*.bak」ファイルが毎日できています これを3日以上経過した*.bakファイルを消去したいので直接 コマンドラインから次のようにキーボードでたたくと正常に消去できます find /root -name '*.bak' -mtime '+3' -exec rm -f {} \; これをシェルスクリプト(sakujyo.bash)として次のように記載し /rootの中におきました ================(sakujyo.bash)===================== #!/bin/bash find /root -name '*.bak' -mtime '+3' -exec rm -f {} \; =================================================== そして # sakujyo.bash と打鍵すると 次のようにエラーとなります -bash: sakujyo.bash: command not found findのpathが通ってないのかとも考えられるのですが・・・ findコマンドは /usr/bin/ の中にあるので次のように記載してみました =================================================== #!/bin/bash cd /usr/bin find /root -name '*.bak' -mtime '+3' -exec rm -f {} \; ==================================================== このようにしても同じエラーとなります 記載方法等教えていただけませんか よろしくお願いします。

  • [シェルスクリプト内で bashコマンド後のコマンドが実行されない]

    [シェルスクリプト内で bashコマンド後のコマンドが実行されない] 以下の様にシェルスクリプトを記述するとコマンド2が実行されません。 #! /bin/sh コマンド1 bash コマンド2 bash環境でコマンド2を実行させるにはどうしたら良いのでしょうか?

  • shのシェルスクリプトの演算方法について・・・

    bashのシェルスクリプトについてもお聞きしたのですが、 その方法では演算ができない!ということになってしまいました・・・ こういうちょこっとしたところが違うのが嫌です・・・↓ bashの場合は ---------- bash$ a=1 bash$ a=$(( $a + 1 )) bash$ echo $a 2 ---------- となったのですが、shではエラーが出てしまいました・・・ また、shのスクリプトもbashのスクリプトもシェルスクリプトと呼ぶのでしょうか?

  • Automatorのシェルスクリプトがエラーの場合

    以前こちらで教えていただいた方法で、Automatorのフォルダアクションのシェルスクリプトによるファイルのコピーを利用しています。 その節はお世話になりましてありがとうございました。 今回相談にのっていただきたいのは、下記のスクリプトでフォルダAにあるファイルをWindows7の中のiCloud Driveにコピーしているのですが、コピーできない時が何度かありました。 原因はWindows7との接続が切れてしまったからなのか、他に原因があるのかわかりませんでした。 そこで、コピーに失敗した場合にその原因をメールで通知することは可能でしょうか? 普段はモニターをつけていないので、メールが届いたらチェックすることができて便利なのですが、ネットで検索してもそのものズバリの解決策は見つけられませんでした。 もしメールでの通知が難しそうであれば、エラーのログを残すなどの方法でも構いませんので、ご指導をよろしくお願いします。 Automatorは下記のような設定です。 1. 指定されたFinder項目を取得(Automatorでのテスト用に設定) 2. フォルダの内容を取得 3.シェルスクリプトを実行 ======================================== #!/bin/bash path=$1 folderC=/Volumes/iCloudDrive cp "${path}" "${folderC}"/ ======================================== Mac OS X 10.8.5 よろしくお願いします。

    • ベストアンサー
    • Mac
  • 簡単なシェルスクリプトを見てください。

    次のようなシェルスクリプトを作成しました。 #!/bin/sh echo [$1] awk '/"0312345678"/ {print}' /test > /test1 awk '/$1/ {print}' /test > /test2 のように簡単なスクリプトです。 $ sample \"0312345678\" で実行させたら、echo では"0312345678"に出力されます。 しかし、test1にはちゃんと結果が入っていますが、test2は空のファイルです。 なぜ、結果が違うのですか? 実行時、引数を渡す方法で、test1と同じ結果を得たければ、どうすればいいのですか?

  • telnet 110 のシェルスクリプトについて

    linux初心者です。 telnet localhost 110 の確認スクリプトを作成したいのですが、調べた結果以下のスクリプトを作成しましたがうまく動作してくれません。 お分かりになる方ご教授ください。 test.sh #!/bin/bash sleep 1; echo user $1 sleep 1; echo pass $2 sleep 1; echo stat sleep 1; echo quit test1.sh #!/bin/bash sh ./test.sh | telnet localhost 110 ./test1.sh hogeuser hogepass 同じアカウントの場合$1 $2の部分にuser名 パスワードを記入すればいいのは分かるのですが、毎回アカウント・パスワードが違うため引数に渡したいと思います。 よろしくお願いいたします。