パスワードを忘れた? アカウント作成
13402803 journal
日記

tuneoの日記: シェル力を高める:シェルスクリプト内部で環境変更 13

日記 by tuneo

スクリプトの中で環境切り替えツールを呼び出して環境変数を変更してるんだけど、その「環境変数を変更」が/etc/profile.d/hogehoge.shの書き換えで実現されている(というか、hogehoge.shは/other/dirに置いてあるスクリプトA.sh, B.sh...などへのシンボリックリンクなので、それを張り替えてる)場合、スクリプト内に変更を反映する処理をきれいにできないものかなーと。

#!/bin/bash
#このへんは旧環境(hogehoge.sh→/other/dir/A.sh)で実行
chgenv --set B # ここでシンボリックリンクが張りなおされる
#これ以降は新環境(hogehoge.sh→/other/dir/B.sh)で実行
source /etc/profile.d/hogehoge.sh
なんちゃらなんちゃら

chgenvを解析した結果、上述のような挙動であることが分かったので、とりあえずはsource hogehoge.shして一応期待通りの動作にはなっているんだけど、もっとこう、あたかも「一度ログアウトして再度ログインしたかのように環境変更を反映させたい」という場合はどうすれば綺麗に書けるだろうか?

bash --login~とか書くと新しいプロセスになっちゃうしなー。……スクリプトの内部でexec bash --loginしたらどうなるんだっけ?

この議論は、tuneo (2938)によって テキとトモのテキ禁止として作成されたが、今となっては 新たにコメントを付けることはできません。
  • ドットコマンドと直接スクリプトを実行した時の違い [hatena.ne.jp]

    ただ、exportでPATHなどにいろいろと追加していると、一度logoutして再度loginした方がすっきりすることも多いですね。

    • ドットは、sourceコマンドの省略記法。既に source コマンドは使われてますね。

        /etc/profile などの「sh がログイン時に自動で読み込むスクリプト」が書き換えられた時に、「ファイルを明示的に指定して source で読み込む」のが美しくない、という話かな、と理解したのですが…

      > スクリプトの内部でexec bash --loginしたら
      そこでスクリプトの実行は止まってしまいます。

      #!/bin/bash
      echo step1
      exec bash -l -c "echo step2"
      echo step3

      こういうコードだと、step1 step2 は表示されますが、step3 は表示されません。

      (環境変数に情報を保持しているとか、ループや条件判断の都合で)どうしても一つのスクリプトで書きたいのであれば、今のやりかたしかないと思いますが

      そういった依存関係がないのなら、たぶん、一番シンプルなのは、それぞれの環境で動かすスクリプトでファイルを分けることじゃないですかね。

      #!/bin/bash
      #このへんは旧環境(hogehoge.sh→/other/dir/A.sh)で実行
      chgenv --set B # ここでシンボリックリンクが張りなおされる
      exec bash -l -c /foo/bar/next.sh

      next.sh:

      #!/bin/bash
      #これ以降は新環境(hogehoge.sh→/other/dir/B.sh)で実行
      なんちゃらなんちゃら

      といった感じで。

      親コメント
      • なるほど。
        ドットとsourceが同じこと、ということ知らずにドットコマンド使っていました。
        よくよく調べてみると、bashでもない純粋のshではドットコマンドしか無いようですが、そういうシステムは今は少ないでしょう。

        それはさておき、tuneo様のもともとやりたかったことは、
        再ログインしないで環境変数をログイン直後の状態に戻す、
        ということでしょうか? 私もそうしたいことはよくありますが、再ログインしてました。

        こういうのはどうでしょうか?
        ログイン直後に
        printenv > ~/envafterlogin.sh
        のようなことして、環境を保存。

        戻したいときは、
        source ~/envafterlogin.sh
        とかする。

        これだと、login直後にはなく、あとから追加された環境変数は削除されません。
        envafterlogin.shに無い変数をunsetする処理をしてから戻せばokかもしれません。
        ただし、実際に試していないので、問題が生じるかもしれません。

        親コメント
        • あたかも「ログアウトしてログインしなおした」ようにする、だけなら

          # exec bash --login

          すればいいのです。解説はmanページなどをどうぞ。

          今回のお題は、それが「自動化すべき作業の一部」だった場合にどうするよ、というものでして。

          親コメント
          • なるほど。
            でも、
            export AAA=123
            exec bash --login
            しても
            echo $AAA
            123
            とAAAは残っています。つまり「ログアウトしてログインしなおした」ようにはなっていないですね。それは私にとっては注意点です。

            というか exec bash --loginを実行するシェル内では、AAAはなくなっているようで、つまりログイン直後の環境になっているみたいですが。

            必要になったときは#3276326や#3276261を参考にさせていただきます。
            ありがとうございました。

            親コメント
            • ガチで「ログアウトして再ログイン」を模倣するなら、親シェルの環境を引き継がないですからね。

              そこまで必要なときはexec /bin/loginかな?ユーザ名・パスワードの入力を要求されますが。

              親コメント
              • よく考えたら、loginした後
                すぐ、
                bash として新しいshellに入り、
                        env1 設定
                        job1 実行
                        exit
                これで元の環境に戻るから、自動的にlogin直後の環境に戻る、
                bash
                        env2 設定
                        job2 実行
                        exit
                として行けば良いような気がしてきました。
                ただ、いろいろやっている間に自分がどの環境にいるのかわからなくなってくるから、promptにでもそれがわかるような印をしておけば、よいのかも。

                自動実行の場合も、基本は上のをそのままスクリプトにすれば bash --login -cは使わなくても何とかできそう。
                bash --login関連で新しいまっさらなシェルが開ければもっと良いのですが。

                親コメント
              • >いろいろやっている間に自分がどの環境にいるのかわからなくなってくるから、

                脇目も振らずに同じttyを注視し続けているという決め付けは必ずしも…
                ということも考えられる。そこで

                >promptを変更して

                以外の選択肢

                ・踏み切りが遮断する・パトランプが鳴動する
                ・syslogに[notice]を飛ばす
                ・猫(ぬいぐるみ可)をアイスホッケーのペナルティボックスに軟禁

                …など、状態を明らかにするべく適宜目印を設定/解除する提案を。

                親コメント
              • そういうのは面白いですね。
                ただ、実装は難しそうというか、大変そう。
                でもやったらオオウケかも。
                なにしろ実際のシェルやスクリプトが動いているのは遠く離れたリモートホストかもしれないですから。

                親コメント
          • >「自動化すべき作業の一部」

            はログイン中のユーザが動作させる前提なのですか?
            そうではなくcrontabにぶち込んでやらせる形式とか
            JP1なんたら(日立のプロプラ、他社にもいろいろあるはず)にやらせる?

            su - $USER "-c $command"
            でも現環境変数引き継ぐんだっけ(solaris の man(1M)ではそう書いてた)

            すっきりと別ユーザのアカウント作るような改修は不許可なんだろな。

            親コメント
            • ログイン中のユーザが動作させるのが前提ですし、別アカウントは作らない方がいいですし、別途ソフトを入れたりもしませんし、そのためのお金も出ません。

              スクリプトぶち込んでしばらく他の事してれば済むので大掛かりなことはする気はないですねというか、日記を書いた時点ですでに最低限の目的は達成した後なんですよ。

              こうやって書いたら動いたけど、もうちょっといい書き方はないものか、と模索をしているだけです。

              親コメント
      • >> スクリプトの内部でexec bash --loginしたら
        >そこでスクリプトの実行は止まってしまいます。
        ですよねー(苦笑)。書いてからハタと気づいて赤面しました。

        親コメント
  • by tuneo (2938) on 2017年09月09日 10時32分 (#3276326) ホームページ 日記

    詳しくはガチ業務の内容になってしまうのでぼやかしてますが、環境定義をやってる/other/dir/*.shは普通に6個とかありまして(場合によってはさらに増える)、私のミッションは各環境で所定の処理を実施し(「なんちゃらなんちゃら」のくだりですね)、結果が正常であることを確認する、というテストなんですね。

    しかも各々の環境下で行う処理はそこそこ手数が多くて時間もかかるため、環境変える→ログアウト→ログイン→面倒で時間のかかる処理→終わるまでぼけーっと待つ→結果確認→最初に戻る、を手作業でくりかえすのがあまりにかったるいという事情がありまして、スクリプト書いて走らせたら離席して一服入れに行くためにやっとるわけです。

    chgenv --listで環境一覧を出せるので、forループの内部でそれぞれの環境に切り替えて処理をする、というようなことをやるために頭を使ったのですが、

    bash --login~とか書くと新しいプロセスになっちゃうしなー。

    は完全に要らぬ心配でした。サブプロセス全然オッケー。なので解決策はこんなん。

    #!/usr/bin/bash
    あれこれいろいろ
    for e in $(chgenv --list)
    do
      chgenv --set $e
      bash --login <<'END'
        じかんのかかるしょり
      END
    done

    で解決です。

typodupeerror

人生unstable -- あるハッカー

読み込み中...