方法1

方法1

Xセッション内で次の手順を実行できます。

  1. ターミナルエミュレータ(Xterm)を開きます。
    • Bashは読んで.bashrc対話型になります。コマンドプロンプトがコマンドを待っています。
  2. 入力するvim 'my|file*' '!another file&'
    • Vimが起動し、表示されmy|file*!another file&編集されます。
  3. によるとCTRL-Z
    • Vimが停止し、Bashプロンプトが再び表示されます。

私は理解できません。台本ステップ3(作業制御)を放棄せずにステップ1と2を実行します。ファイルをパラメータとして受け取ります。

script 'my|file*' '!another file&'

助けてください?

スクリプトは、選択したテキストファイルを引数として提供することによってファイルマネージャによって実行されます。

心配しないでください。私は合理的であり、通常はファイル名をそのように指定しません。一方、*!&|$<newline>...ファイル名に特殊文字()が表示されている場合は、スクリプトを中断しないでください。

私は具体的な例を挙げるためにVimを使用しています。ターミナルで対話的に実行され、引数を受け取る他のプログラムは、このソリューションの利点を享受できます。


私の試み/研究

xterm -e vim "$@"

明らかに失敗しました。 Xtermにはシェルはありません。

Supershellにすぐに戻らず、初期コマンドを使用して対話型bashサブシェルを実行します。 有望に見えます。それに対する答えは、.bashrcBashのソースとして(代わりに)別のファイルを指定する方法を説明します。だから私はこれを作りました~/.vimbashrc

. ~/.bashrc
set -m
vim

今電話してください

xterm -e bash --init-file ~/.vimbashrc

その結果、Bashと中断可能なVimを含む新しいターミナルが誕生しました。しかし、Vimが開くファイルを指定する方法はわかりません。

答え1

私はいくつかのアプローチを考えることができますが、最初のアプローチはBashであまり陳腐だと思います。主に(私に)扱いやすく、珍しい点があるようです。しかし、これも好みの問題かもしれないので、両方とも取り上げます。

方法1

「事前参照」方法

作ることも含まれるあなたのスクリプト$@内部対話の代わりにこれを行うために配列を拡張し、ファイルシステムのファイル記述子機能(システムで利用可能な場合)を引数としてbash使用できます。これらのファイル記述子は、次のようにファイル名を処理する文字列を参照できます。/dev/fd/X--init-file

xterm -e bash --init-file /dev/fd/3 3<<<". ~/.bashrc; set -m; vim $@"

ファイルシステムに対するこのファイル記述子のトリックの1つの利点は、ユーザーなどの外部ヘルパーファイルに依存しないため、自己完結型のソリューションがあることです。これは拡張のためにコンテンツが動的である.vimbashrcため、ここで特に便利です。--init-file$@

一方、スクリプトから内部シェルまで、ファイル記述子の実際の永続性に注意が必要な場合があります。このトリックは、介入するプロセスが親プロセスから受け取ったファイル記述子を閉じない限り、うまく機能します。これは多くの標準ツールの一般的な動作ですが、受信したsudoすべてのファイルディスクリプタを閉じるデフォルトの動作の途中にある場合、このトリックは機能せず、一時ファイルまたは生ファイルを使用する必要があります.vimbashrc

とにかく$@上記のアプローチでは、ファイル名にスペースや改行文字が含まれている場合は機能しません。内部的には、bashこれらのファイル名はコマンドシーケンスを使用するときに引用されないため、ファイル名のスペースは標準の解釈に従って単語区切り文字として機能するためです。

@Q$@この問題を解決するために、Bash 4.4以降では、次のように配列にパラメータ変換構文を使用できます。

xterm -e bash --init-file /dev/fd/3 3<<<". ~/.bashrc; set -m; vim ${@@Q}"

Bash 4.4以前では、これを使用して同じ結果を得ることができますprintf %q(より読みやすくするためにHere Documentとして使用しますが、上記のHere Stringと同じことを行います)。

printf -v files ' %q' "$@"
xterm -e bash --init-file /dev/fd/3 3<<EOF
. ~/.bashrc
set -m
vim $files
EOF

ただし、設定によっては対話型シェルに対するBashの標準動作であるため、ユーザーの前にソーシングすること/etc/bash.bashrcを検討することもできます。.bashrc

また、このコマンドは元のスクリプトに慣れておらず無害であるため、実際には重複しますが、相互作用が暗示されるため、ここではset -m実際に重複します。つまり、文字通り質問のタイトルを理解したら、ジョブ制御シェルが欲しいでしょう。しかし、完全なインタラクティブシェルではないので必要です。持つ--init-filebash-m行動の違い

方法2

-sオプション

Bash(およびPOSIX)-sオプションを使用すると、非対話型シェルと同様に、対話型シェルの引数を指定できます1。したがって、このオプションをスタンドアロンソリューションとして使用すると、-s次のようになります。

xterm -e bash --init-file /dev/fd/3 -s "$@" 3<<'EOF'
. ~/.bashrc
set -m  # superfluous as bash is run with `--init-file`; you would instead need it for a job-controlling yet "non-interactive" bash (ie no `--init-file` nor `-i` option)
exec <<<'exec < /dev/tty; vim "$@"'
EOF

注目すべき奇妙な点:

  1. Here ドキュメントの区切り記号仕様〜しなければならない一重引用符で囲む必要があります。そうでなければ、ここのドキュメントの内部セクションは、適切な引用符なしでスクリプトが属する内部エントリではなくスクリプト$@によって拡張されます。bashこれは、文書の区切り文字を意図的に引用しない「事前引用」アプローチとは対照的です。
  2. ここの文字列(exec <<<...標準入力リダイレクト部分)〜しなければならないさらに、一重引用符タイプ2または配列がまだ埋め込まれていない場合、"$@"一部は内部で拡張されます。bash$@
  3.  特に、配列を完全に埋める必要があるコマンドの実行をexec <<<内部的に「遅延」するには、ヘルパーとして標準入力リダイレクト(ここで文字列を介したリダイレクト)が必要です。bash$@
  4. これらのヘルパーのstdinリダイレクト(Here Stringセクション)では、bash内部リダイレクトを独自のstdinに再作成する必要があります。今回は制御端末(したがってこのexec < /dev/tty行)に戻り、インタラクティブ機能を再開できるようにする必要があります。
  5. 私たちはする必要がありますみんな注文する意味は指定後に実行exec < /dev/tty(例:ここ)vim "$@"同じ行に3exec < /dev/ttyこれらのリダイレクトの後、ここでは文字列4を読み取ることができないからです。実際、この特定のコードスニペットがこの例のように十分に短い場合は、Here Stringとして使用する方が良いでしょう。

このアプローチはおそらくあなたのような外部ヘルパーファイルで使用するのに適しています.vimbashrc(スタンドアロンの利便性はあきらめますが)。なぜなら、そのようなファイルの内容は、ファイル名パラメータの問題に関する限り、完全に静的である可能性があるからです。これにより、ファイルマネージャが呼び出すスクリプトは次のように簡単になります。

xterm -e bash --init-file .vimbashrc -s "$@"

その仲間は.vimbashrc次のとおりです。

. ~/.bashrc
#  (let's do away with the superfluous `set -m`)
exec <<<'exec < /dev/tty && vim "$@"'  # let's also run vim only if the redirection to the controlling tty succeeded

添付ファイルにはまだいくつかの欠点がありますが、「クリーンアップ」の考慮事項を除いて、後者のバージョン(スタンドアロンではない)の可能な利点の1つは、ファイルマネージャがコマンド全体xterm -e ...(partを除く"$@")を直接使用できることです。 "スクリプト",もし非常に素晴らしいコマンドを指定することができ、ファイル名引数と一緒に正式な「argv」配列を生成するために丁寧にスペースに分割されます。

-sさらに、すべてのバージョンのメソッド全体がデフォルト.bash_historyで使用されます。助ける人stdinリダイレクトがあるので、その特定の部分をできるだけ長く維持してきました。もちろん。unset HISTFILE--init-file

-

比較すると、環境変数で指定されたスクリプト配列がいっぱいになるdashため、この方法を使用する方が便利なため、スタンドアロンソリューションは非常に簡単です。dash$@ENV

xterm -e env ENV=/dev/fd/3 dash -s "$@" 3<<'EOF'  # `dash` run through `env` just to be positive that `ENV` is present when dash starts
vim "$@"
EOF

ファタイ


1オプションを使用してシェルを呼び出すときとは異なり、指定できないことは例外です。$0-c

2ここで追加の文書を使用する場合は、対応する区切り文字も引用する必要があります。

3は実際には同じ"complete_command"にあります。POSIXで定義つまり、同じ「complete_command」の一部である限り、複数行にまたがる可能性があります(たとえば、行に行連続バックスラッシュがある場合、または複合ブロック内にある場合など)。

4これはおそらく最初の短い段落から派生した標準動作でなければなりません。このゾーン

答え2

簡単な概念証明で以下を試しました。

. ~/.bashrc
set -m
vim $arg

そして:

arg=file-to-edit xterm -e bash --init-file vimbashrc

これはユーザーが指定したようにある程度機能し、対話型コマンドに引数を渡すことができます。

したがって、一時ファイルを使用すると、次のような結果が発生する可能性があります。

#!/bin/bash

if [ "$1" = "" ] ; then
        . ~/.bashrc
        set -m
        (IFS=$'\n'; vi $(cat "$tmpfile"))
else
    tmpfile=$(mktemp /tmp/vimstart.XXXXXX)
    for arg in "$@" ; do
        echo "$arg" >> $tmpfile
    done
    tmpfile=$tmpfile xterm -e bash --init-file ./vimstart
    rm "$tmpfile"
fi

ファイル名にが含まれる可能性があるため、\nNULL文字を区切り文字として使用することもできます。

#!/bin/bash

if [ "$1" = "" ] ; then
    . ~/.bashrc
    set -m
    (IFS=$'\00'; vi $(cat "$tmpfile"))
else
    tmpfile=$(mktemp /tmp/vimstart.XXXXXX)
    for arg in "$@" ; do
        echo -n "$arg" >> $tmpfile
                echo -n $'\x00' >> $tmpfile
    done
    tmpfile=$tmpfile xterm -e bash --init-file ./vimstart
    rm "$tmpfile"
fi

関連情報