(「スーパー」)シェルにすぐに戻ることなく、初期コマンドで対話型bashサブシェルを実行します


87

bashサブシェルを実行し、(1)いくつかのコマンドを実行し、(2)その後、そのサブシェルに残りたいと思います。これらのそれぞれを個別に行うことができます。

  1. -cフラグを使用してコマンドを実行します。

    $> bash -c "ls; pwd; <other commands...>"

    ただし、コマンドが実行されるとすぐに「スーパー」シェルに戻ります。インタラクティブなサブシェルを実行することもできます。

  2. 新しいbashプロセスを開始:

    $> bash

    そして、明示的に言うまでサブシェルを終了しません...しかし、初期コマンドは実行できません。私が見つけた最も近い解決策は次のとおりです。

    $> bash -c "ls; pwd; <other commands>; exec bash"

    これは機能しますが、1つのサブシェルで指定されたコマンドを実行し、対話のために別のコマンドを開くため、私が望んだ方法ではありません。

これを1行で行いたい。サブシェルを終了したら、問題なく通常の「スーパー」シェルに戻る必要があります。道があるはずです~~

NB:私が尋ねていないことは...

  1. bashのmanページの入手先を尋ねない
  2. ファイルから初期化コマンドを読み取る方法を尋ねません...私はこれを行う方法を知っています、それは私が探している解決策ではありません
  3. tmuxまたはgnu画面の使用に興味がない
  4. これにコンテキストを与えることに興味がない。すなわち、質問は一般的なものであり、特定の目的のためではありません
  5. 可能であれば、私が望んでいることを達成するような回避策の使用を避けたいのですが、「汚い」方法で。これを1行で行いたいだけです。特に、次のようなことはしたくないxterm -e 'ls'

Expectのソリューションは想像できますが、それが望みのワンライナーではありません。このexec bashソリューションはどのような点であなたに不適当ですか?
グレンジャックマン

@glennjackman申し訳ありませんが、私は専門用語に精通していません。「期待ソリューション」とは何ですか?また、exec bashソリューションには2つの個別のサブシェルが含まれます。連続したサブシェルが1つ必要です。
SABBATINIルカ

3
の利点、最初のサブシェルを2番目のサブシェルexec置き換えるため、親の下に1つのシェルしか残らないということです。初期化コマンドが環境変数を設定する場合、それらは実行されたシェルに存在します。
グレンジャックマン


2
そして、の問題は、execあなたがそのような非エクスポート変数、関数、エイリアス、...のように、環境を経由してサブシェルに渡さいない何かを失うということです
カート・J・サンプソン

回答:


77

これは、一時的な名前付きパイプ使用して簡単に実行できます。

bash --init-file <(echo "ls; pwd")

この答えの功績は、リー・ライアンのコメントにあります。これは本当に便利だと思ったのですが、コメントではあまり目立たなかったので、独自の答えにすべきだと思いました。


9
これはおそらく$HOME/.bashrc実行されないことを意味します。一時的な名前付きパイプから含める必要があります。
ウブロ

9
明確にするために、次のようにしますbash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
。– Hubro

5
これはひどいですが、機能します。bashがこれを直接サポートしていないとは思えません。
パットニーマイヤー

2
@Gusは、.同義語ですsource:コマンドss64.com/bash/source.html
ジョナサン・ポッター

1
sudo bash --init-file <(echo "ls; pwd")またはなどのユーザー切り替えで動作させる方法はありsudo -iu username bash --init-file <(echo "ls; pwd")ますか?
jeremysprofile

10

一時ファイルを使用すると、これを迂回的に実行できますが、2行かかります。

echo "ls; pwd" > initfile
bash --init-file initfile

素敵な効果を得るには、一時ファイルに含めることで一時ファイルを削除することができますrm $BASH_SOURCE
エドゥアルドイヴァネック

エドゥアルド、ありがとう。これは素晴らしい解決策ですが、...これはファイルI / Oをいじらずにできないと言っているのでしょうか。これが自己完結型のコマンドとして保持されることを好む明白な理由があります。なぜなら、ファイルがミックスに入ってくる瞬間に、ランダムな一時ファイルを作成する方法について心配し始め、次にあなたが言及したようにそれらを削除する必要があるからです。厳密になりたいのであれば、このようにもっと多くの努力が必要です。したがって、よりミニマルでエレガントなソリューションが望まれます。
SABBATINIルカ

1
@SABBATINILuca:私はそのようなことを言っていません。これは単なる方法でありmktemp、@ cjcが指摘した一時ファイルの問題を解決します。Bash 、stdinからのinitコマンドの読み取りをサポートできますが、私が知る限り、サポートしていません。-初期化ファイルとして指定し、それらを半分パイプすることはできますが、Bashは終了します(おそらくパイプラインを検出したため)。エレガントなソリューション、IMHOはexecを使用することです。
エドゥアルドイヴァネック

1
これは通常のbashの初期化もオーバーライドしませんか?@SABBATINILucaこれで何を達成しようとしているのですか?なぜシェルを自動起動していくつかのコマンドを実行し、そのシェルを開いたままにする必要がありますか?
-user9517

11
これは古い質問ですが、Bashは次の構文を使用して一時的な名前付きパイプを作成できます。bash--init-file <(echo "ls; pwd")。
ライライアン

5

代わりにこれを試してください:

$> bash -c "ls;pwd;other commands;$SHELL"

$SHELL シェルを対話モードで開き、で閉じるまで待機しexitます。


9
参考までに、これは後で新しいシェルを開くため、コマンドのいずれかが現在のシェルの状態に影響する場合(ファイルのソースなど)、期待どおりに動作しない可能性があります
-ThiefMaster

3

私が言及した「Expectソリューション」は、Expectプログラミング言語を使用してbashシェルをプログラミングすることです

#!/usr/bin/env expect
set init_commands [lindex $argv 0]
set bash_prompt {\$ $}              ;# adjust to suit your own prompt
spawn bash
expect -re $bash_prompt {send -- "$init_commands\r"}
interact
puts "exiting subshell"

あなたはそれを次のように実行します: ./subshell.exp "ls; pwd"


これには、コマンドを履歴に登録するという利点もあると思いますが、間違っていますか?また、この場合、bashrc / profileが実行されているかどうか興味がありますか?
muhuk

これにより、コマンドを履歴に入れることができることを確認しましたが、他のソリューションではできません。これは、.screenrcファイルでプロセスを開始するのに最適です。開始したプロセスを終了しても、画面ウィンドウは閉じません。
ダンサンドバーグ

1

なぜネイティブサブシェルを使用しないのですか?

$ ( ls; pwd; exec $BASH; )
bar     foo     howdy
/tmp/hello/
bash-4.4$ 
bash-4.4$ exit
$

コマンドを括弧で囲むと、bashがこれらのコマンドを実行するサブプロセスを生成するため、たとえば、親シェルに影響を与えずに環境を変更できます。これは基本的に、より読みやすい同等bash -c "ls; pwd; exec $BASH"です。

それでも冗長に見える場合は、2つのオプションがあります。1つは、このスニペットを関数として使用することです。

$ run() { ( eval "$@"; exec $BASH; ) }
$ run 'ls; pwd;'
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$ run 'ls;' 'pwd;'
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$

もう1つは、exec $BASH短くすることです。

$ R() { exec $BASH; }
$ ( ls; pwd; R )
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$

R文字列をエスケープして遊ぶ必要がないので、個人的にアプローチが好きです。


1
OPが念頭に置いているシナリオでexecを使用して注意事項があるかどうかはわかりませんが、これは文字列エスケープの問題のないプレーンなbashコマンドを使用するため、私にとってこれがはるかに最適なソリューションです。
ピーター

1

sudo -E bashうまくいかない場合、私は以下を使用します。

sudo HOME=$HOME bash --rcfile $HOME/.bashrc

HOME = $ HOMEを設定するのは、新しいセッションでrootのHOMEではなく、ユーザーのHOMEを設定する必要があるためです。これは一部のシステムではデフォルトで行われます。


0

よりエレガントでは--init-fileありませんが、おそらくよりインストルメント化可能です

fn(){
    echo 'hello from exported function'
}

while read -a commands
do
    eval ${commands[@]}
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.