1

私は、キーボードから定期的にユーザー入力を受け取るひどいネストされたスクリプト構造を維持しており、この入力を自動化するスクリプトを作成しようとしています。基本的に、スクリプトの構造は次のようになります。

  • cmd1.csh は 3 行の入力を受け取り、cmd2.csh を呼び出し、正常に終了します。
  • cmd2.csh は cmd3.pl を 2 回呼び出し、1 行の入力を取得してから正常に終了します。
  • cmd3.pl は 1 行の入力を取り、正常に終了します。

残念ながら、これらのスクリプトを永続的に変更することはできないため、できることはすべて、スクリプト チェーン全体のラッパーとして実行する必要があります。

次の perl スクリプトのいくつかのバリエーションを試してみましたが、引用符とエスケープの魔法の組み合わせに達していないだけかもしれません。

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/pltest/cmd1.csh";

system_tcsh

sub system_tcsh
{
   @args = ("tcsh", "-c", shift);
   return system(@args);
}

問題は、cmd3.pl が 1 回呼び出されて d が読み込まれると、cmd3.pl への 2 回目の呼び出しまたは cmd2.csh によって入力が読み取られなくなることです。複数の読み取りを cmd3.pl に入れると問題なく動作し、STDIN からより多くの項目が読み取られますが、cmd3.pl への最初の呼び出しが完了すると、STDIN は空になります (または、何らかの使用不可能な状態になります)。

perl スクリプトが戻ったときに STDIN はどうなりますか? perl などを使用してこの種の入力をシミュレートする方法はありますか?

前もって感謝します。

4

5 に答える 5

3

問題を再現するための条件を作成しましたが、すべてうまくいきました。

caller.pl:

#! /usr/bin/perl

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/doc/Answers/src/pltest/cmd1.csh";

sub system_tcsh
{
   @args = ("tcsh", "-c", $cmd);
   return system(@args);
}

system_tcsh

cmd1.csh:

#! /bin/csh
echo "${0}(cmd1) $argv[*]"
set line1 = $<
set line2 = $<
set line3 = $<
setenv lineno  3
echo "cmd1: read: $line1 $line2 $line3"
./cmd2.csh

cmd2.csh:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
./cmd3.csh
./cmd3.csh

set line6 = $<

echo "    ${0}: Read: $line6"

cmd3.csh:

#! /bin/csh
# cmd3
set line = $<
echo "        ${0}: Read: '$line'"

テスト走行:

frayser@gentoo ~/doc/Answers/src/pltest $ ./caller.pl 
/export/home/frayser/doc/Answers/src/pltest/cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.csh: Read: 'd'
        ./cmd3.csh: Read: 'e'
    ./cmd2.csh: Read: f

たぶん、これを変更して問題を再現したり、ソリューションを実装するために使用したりできます。

-

UPDATE
これは、Perl STDINの問題を再現し、その回避策を提供する更新です。

cmd3をPerlバージョンに置き換えると、報告された問題のある結果が得られます。

cmd3.cshを置き換えるcmd3.pl:

#! /usr/bin/perl
# cmd3

$_=<STDIN>;
chomp();
print "        $0: Read: '$_'\n";

結果:cmd3.plを使用します。「d」が読み取られると、それ以上の入力は利用できなくなります。

./caller.pl    
./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: ''
    ./cmd2.csh: Read: 

この状況を改善するために、cmd2は1行のみをcmd3に送信するように変更されています。(1)コマンドはこれを簡単に実行します。

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
line | ./cmd3.pl
line | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

これは、ラインコマンドを実行するオーバーヘッドを回避するために最適化されたcmd2.cshです。

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
set x = $<
echo $x | ./cmd3.pl

set y = $<
echo $y | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

これは、更新されたcmd2.cshの出力です。機能は、Perlを使用 した場合と最終スクリプトとしてcshを使用した場合と同じになりました。stdinが失われることはありません。

./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: 'e'
    ./cmd2.csh: Read: f
于 2010-09-29T22:23:42.860 に答える
1

まず、Expectを使用して入力をスクリプト化します。これはおそらくより信頼性が高く、ほとんどの Unix システムで利用できます。

echo のバージョンによっては、多くの場合、引用符で囲まれた文字列で "\n" を使用できますが、機能的には同等であるはずです。何かのようなもの:

$cmd = '/bin/echo -e "a\nb\nc\nd\ne\nf" | ~/pltest/cmd1.csh'

(これは私のLinuxボックスで動作します..man echoローカルバージョンで何ができるかを確認します。)

しかし、Perl スクリプトがどのように入力を読み取っているかを実際に見なければ、出力がどこに向かうのかを正確に推測することは難しく、これは確かに非常に複雑な状況です。

于 2010-09-28T17:08:45.627 に答える
0

@just jon:残念ながら、expectは私には利用できません。私は、自分が持っているツールを使って方法を見つけようとしていました(リクエストプロセスが正確に適切ではないため)。でも、返事をありがとう!perlスクリプトは、または<>を実行しているだけで、どちらも正しく機能していないようです。

@ user268396:それも機能していないようです。データの送信元に関係なく、パイプが入力をSTDINに強制するため、機能的には同等だと思います。

@Frayser:3番目のスクリプト(cmd3.csh)をperlスクリプトとして試しましたか?それが私の問題です。

于 2010-10-04T17:19:47.357 に答える
0

Greg Bacon の貪欲な perl stdin に関するこの回答は、これを解決する方法のようです。もちろん、一番奥のperlスクリプトを変更しなければならないということですが、変更の許可が得られれば、今のところ最善の解決策です。これは最も堅牢なソリューションではありません。おそらく、Expect pm を使用するのが最善ですが、それを除けば、これでうまくいくはずです。

于 2010-10-05T16:06:18.003 に答える
0

そして、次のようにするとどうなりますか:

echo 'a\
b\
c\
d\
e\
f' > tmpfile
cat tmpfile | ~/pltest/cmd1.csh

それが機能する場合は、入力をプリフェッチし、それを一時ファイルに保存してから、上記のcat /path/to/tempfile | /path/to/cmd1.cshトリックを実行するように Perl スクリプトを編成できます。

于 2010-09-28T03:04:05.357 に答える