サブシェルを使用せずにシェルコマンド置換を実行することは可能ですか?


11

サブシェルを使用せずにコマンド置換を要求するシナリオがあります。私はこのような構成を持っています:

pushd $(mktemp -d)

次に、一時ディレクトリを一度に終了して削除します。

rmdir $(popd)

ただしpopd、ポップされたディレクトリが返されない(新しい現在の現在のディレクトリが返される)ため、およびサブシェルで実行されるため、これは機能しません。

何かのようなもの

dirs -l -1 ; popd &> /dev/null

ポップされたディレクトリを返しますが、次のようには使用できません。

rmdir $(dirs -l -1 ; popd &> /dev/null)

これはpopdサブシェルにのみ影響するためです。必要なのは、これを実行する機能です。

rmdir { dirs -l -1 ; popd &> /dev/null; }

しかし、それは無効な構文です。この効果を達成することは可能ですか?

(注:一時ディレクトリを変数に保存できることはわかっています。そうする必要をなくし、プロセスの中で何か新しいことを学ぼうとしました!)


1
ディレクトリを変数に保存します。このようにしてtrap、プロセスがシグナルによってポークされた場合、ハンドラーはディレクトリをクリーンアップできます。
2016

答えはノーです。
h0tw1r3

fishコマンド置換に相当するonのように見え()、外殻のフォルダーを変更します。通常は煩わしいですが、このような場合には便利です。
トリシス

回答:


10

質問のタイトルの選択は少し混乱しています。

pushd/ popd、およびcshによってコピーされた機能は、記憶されたディレクトリのスタックを管理する方法です。bashzsh

pushd /some/dir

プッシュ現在のスタックに作業ディレクトリを、そしてその後、現在の作業ディレクトリを変更し(そして印刷/some/dirそのスタック(スペースで区切られた)の内容が続きます。

popd

スタックのコンテンツを出力し(ここでもスペースで区切られます)、スタックの一番上の要素に変更し、スタックからポップします。

(また、いくつかのディレクトリはそこに~/xまたはの~user/x表記で表されることに注意してください)。

したがって、スタックに現在/aとがある/b場合、現在のディレクトリはで/hereあり、実行中です。

 pushd /tmp/whatever
 popd

pushd印刷されます/tmp/whatever /here /a /bpopd意志出力/here /a /b、ありません/tmp/whatever。これは、コマンド置換の使用とは無関係です。popd以前のディレクトリのパスを取得するために使用することはできません。一般に、その出力を後処理することはできません(ただし、そのディレクトリスタックの要素にアクセスするには、一部のシェルの$dirstackor $DIRSTACK配列を参照してください)。

多分あなたが欲しい:

pushd "$(mktemp -d)" &&
popd &&
rmdir "$OLDPWD"

または

cd "$(mktemp -d)" &&
cd - &&
rmdir "$OLDPWD"

しかし、私は使用します:

tmpdir=$(mktemp -d) || exit
(
  cd "$tmpdir" || exit # in a subshell 
  # do what you have to do in that tmpdir
)
rmdir "$tmpdir"

いずれにせよ、サブシェルでpushd "$(mktemp -d)"は実行さpushdれません。その場合、作業ディレクトリを変更できませんでした。それmktempはサブシェルで実行されます。別のコマンドであるため、別のプロセスで実行する必要があります。出力はパイプに書き込まれ、シェルプロセスはパイプの反対側でそれを読み取ります。

コマンドが組み込まれている場合、ksh93は個別のプロセスを回避できますが、それでも、通常はフォークによって提供される個別の環境に依存するのではなく、サブシェル(異なる作業環境)であり、今回はエミュレートされます。たとえばksh93a=0; echo "$(a=1; echo test)"; echo "$a"では、フォークは関係しませんが、それでもecho "$a"出力します0

ここで、の出力をmktemp変数に保存する場合、それをpushdに渡すと同時にzsh、を使用して次のようにできます。

pushd ${tmpdir::="$(mktemp -d)"}

他のボーンのようなシェルの場合:

unset tmpdir
pushd "${tmpdir=$(mktemp -d)}"

または、$(mktemp -d)明示的に変数に保存せずに数回の出力を使用するには、zsh無名関数を使用できます。

(){pushd ${1?} && cd - && rmdir $1} "$(mktemp -d)"

私は理解しpushdpopdあなたが説明するように動作し、それらはコマンド置換とは独立している-そこに混乱はない!しかし、あなたは自分の問題を明らかにすることで答えました$OLDPWD。できるよpopd; rmdir $OLDPWD。それが私の問題への答えです-他のすべては私が思ったことを確認するだけです。コマンド置換はそれを解決する方法のように見えますが、それはサブシェルのためではなく、サブシェルなしではコマンド置換を実行できないので、OLDPWDを明らかにしてくれてありがとう-それはまさに私が必要としていることです!
-starfry

@starfry、rmdir $(popd)原因popd、それは現在のディレクトリを変更しないことを意味しますサブシェルで実行するが、それはサブシェルで実行されなかった場合でも、出力はpopd一時ディレクトリではありません、それはスペースが区切りリストになりますその一時ディレクトリを含まないディレクトリの。あなたが混乱していると私が言っているところです。
ステファンChazelas

しかし私は私の質問でそれを言ったと思った:「popdはポップされたディレクトリを返さない(新しい現在の現在のディレクトリを返す)、またサブシェルで実行されるため。」確かにリストを返しますが、リストの最初は私が参照していたものです。
starfry

@starfry、申し訳ありません。私が質問のタイトルに戸惑い、残りを十分に注意深く読んでいなかったとしましょう。
ステファンChazelas

よくあなたの答えはまだよく、私を正しい方向に向けました。そのシェル変数を知りませんでしたOLDPWD。ある日man bash、エンドツーエンドで読んだことを忘れないでください!
-starfry

2

ディレクトリを離れる前に、まずリンクを解除することができます。

rmdir "$(pwd -P)" && popd

または

rmdir "$(pwd -P)" && cd ..   # yes, .. is still usable

ただしpushd、これpopdは実際にはインタラクティブシェル用のツールであり、スクリプト用ではないことに注意してください(そのため、おしゃべりです。実際のスクリプトコマンドは、成功しても沈黙しています)。


0

bashでdirs、pushd / popdメソッドによって記憶されたディレクトリのリストを提供します。

また、dirs -1リストに含まれる最後のディレクトリを出力します。

したがって、を実行して以前に作成したディレクトリを削除するには、次のコマンドpushd $(mktmp -d)を使用します。

rmdir $(dirs -1)

そしてpopd、リストからすでに削除されたディレクトリ:

popd > /dev/null

すべて1行で:

rmdir $(dirs -1); popd > /dev/null

また、オプション(-l)を追加して、~/xまたは~user/x表記を回避します。

rmdir $(dirs -l -1); popd > /dev/null

これは、あなたが要求した行に著しく似ています。によるエラー報告を隠すため
、私は使用&>しませんpopd

注:そのrmdir時点でのディレクトリなので、ディレクトリは残りpwdます。また、popdコマンドの後に実際にはリンクが解除されます(残りのリンクは使用されません)。


変数「OLDPWD」をサポートするシェル(ほとんどのボーンのようなシェル:ksh、bash、zsh have $OLDPWD)には、使用するオプションがあります。kshはデフォルトでdirs、pod、pushdを実装していないことに注意してください(lksh、dashなどもpopdを使用できないため、ここでは使用できません)。

popd && rmdir "$OLDPWD"

または、より慣用的なもの(上記と同じシェルのリスト):

popd && rmdir ~-

これに関する問題、そして私が解決しようとしていたことはrmdir、これを行うときにあるはずの現在のディレクトリができないことです。を実行するpopd前にに影響を与える必要rmdirがありますが、何を削除するかを知る必要があり、そのことpopdは通知されません。私もあなたと同じようにdirs -l -1答えだと思っていましたが、実際に使うのが答えだということに気づきました$OLDPWD
starfry

@starfry一番短い答えは次のようにすることだと思いますpopd && rmdir ~-

@starfry現在のディレクトリが空であれば(削除を強制せずに)実際に削除しても問題ありません。あなたもrmdir "$PWD"大丈夫と呼ぶことができます。また、現在のディレクトリは、mktmp -dpushd $(mktemp -d)事前に実行された場合)によって作成されpushd、pwd が移動するディレクトリでなければなりません。だから、はい、popdの pushdに、作成されたディレクトリはに保存され$(dirs -1)ています。問題はありません。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.