「test」の2つの表現をXORする方法は?

「test」の2つの表現をXORする方法は?

ディレクトリ(と呼ばれる)に2つのファイル(と呼ばれるdir)のうちの1つが含まれていますが、ファイルが1つもなく両方が含まれていないことを確認する必要があります。fileafileb

理想的な解決策は、述語間でXOR演算を使用することです。

if [ -f dir/filea ] ^ [ -f dir/fileb]
then
    echo Structure ok
    # do stuff
fi

ただし、シェルはこれを^XOR演算子としてサポートしておらず、[オプションなしでコマンドと一緒に-Xまたは--xor同様の負の-a同等-o性を使用することも機能しません。

if ! [ -f dir/filea -eq -f dir/fileb ]
# or
if ! [ -f dir/filea = -f dir/fileb ]

このような本格的なAND / OR式を使用せずにこれを達成する方法はありますか?

if { [ -f dir/filea ] || [ -f dir/fileb ]; } && ! { [ -f dir/filea ] && [ -f dir/fileb ]; }

最後の表現は読みにくくなり、確かに私の実際のパスはdir/fileX

編集する:POSIX準拠のバージョンを目指していますが、他のシェルに関連する拡張にはオープンです(主に好奇心が強いため、sh他のプロジェクトでまたはbash使用しているためksh93便利です)。

答え1

test ...または、コマンドの終了コード[ ... ]はテスト結果です。変数を使用して個々のテスト結果を保存し、後で比較することができます。

[ -f dir/filea ]
testA=$?
[ -f dir/fileb ]
testB=$?
if [ "$testA" -ne "$testB" ]
then
   echo "exactly one file"
else
   echo "both files or none"
fi

これにより、[両方のテストでゼロ以外の終了コードが生成される可能性があります。
仕様によるとhttps://pubs.opengroup.org/onlinepubs/007904875/utilities/test.html、終了コードは>1「エラーが発生しました」を意味します。[エラーが報告されたときに何が起こるかを定義する必要があります。

これを防ぐには、次のような条件付き変数割り当てを使用できます。コサロナンダの答え...

testA=0
testB=0
[ -f dir/filea ] || testA=1
[ -f dir/fileb ] || testB=1
if [ "$testA" -ne "$testB" ]
then
   echo "exactly one file"
else
   echo "both files or none"
fi

...またはコメントに記載されているように、否定を使用して値が0orであることを確認してください1
(「2.9.2パイプライン」 - 「終了ステータス」を参照)https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_02)

! [ -f dir/filea ]
testA=$?
! [ -f dir/fileb ]
testB=$?
if [ "$testA" -ne "$testB" ]
then
   echo "exactly one file"
else
   echo "both files or none"
fi

どちらのバリエーションも「ファイルが存在しません」と同じ方法でエラーを処理します。

答え2

以下は、^sum値に算術XOR演算子を使用します。そのファイルが存在し、通常のファイルの場合、この変数は1、それ以外の場合は0です。ab

a=0
b=0

[ -f dir/filea ] && a=1
[ -f dir/fileb ] && b=1

[ "$(( a ^ b ))" -eq 1 ] && echo OK

別の方法は、テストが成功した回数を計算することです。

ok=0

[ -f dir/filea ] && ok=$(( ok + 1 ))
[ -f dir/fileb ] && ok=$(( ok + 1 ))

[ "$ok" -eq 1 ] && echo OK

答え3

複合コマンドには他のコマンドと同様に終了状態があるため、ほとんどのC類似言語とif a; then b; else c; fi同じ方法で使用できることに注目する価値があります。a ? b : c

a^bχをa ? !b : bシェルに変換すると、次のような結果が得られます。

if if [ -f fileA ]
   then ! [ -f fileB ]
   else   [ -f fileB ]
   fi
then
   echo ONE file present
else
   echo NO or BOTH files present
fi

(参考にif ifしてくださいいいえ太田。 )

テストの1つを繰り返す必要があることは認めます。したがって、反転以外は同じであることを明確にするために垂直に整列しました!

利点は、$?一時変数や同様の変数を保持することによる混乱を避け、(ほとんど)パフォーマンスが良いことです。

-a最後に、コマンドのオプションとオプションに問題があることを指摘したいと思います。解析が不明瞭になり、誤った結果が生じる可能性があります。またはで区切られた2つのテストコマンドを使用できます。-otest&&||

答え4

別のオプションはsupportを使用することですxor。たとえば、perlを使用することです。

$ mkdir dir
$ touch dir/filea
$ perl -le 'print (((-f $ARGV[0]) xor (-f $ARGV[1])) ? 0 : 1)' dir/filea  dir/fileb
0

もちろん、コマンド置換を使用して出力を変数としてキャプチャすることもできます。

または、結果を終了コードとして希望する場合if&&またはとともに直接使用することもできます||

$ perl -le 'exit (((-f $ARGV[0]) xor (-f $ARGV[1])) ? 0 : 1)'  dir/filea  dir/fileb
$ echo $?
0

注:Perlのtrue(0以外/空ではない/定義)とfalse(0 /空の文字列/未定義)の定義は、シェルのtrue(0)またはfalse(非0)定義と同じです。ポイントを認識することが重要です。ヤング)。そのため、上記の三項演算子は、XORがtrueと評価された場合は0を返し、falseと評価された場合は1を返すように書かれています。

このような単純なスクリプトではそれほど重要ではありませんが、Perlスクリプトでさらにブール計算を実行するには、true / falseの最後の瞬間になるまでPerlのtrueおよびfalse定義を使用する必要があります。シェルの期待値を返しました。

ところで、これがちょっと変だと思ったら、そうです。これは。しかし、それは意味があり、その理由があります。しかし、シェルは例外です。ほとんどの言語はtrueをゼロ以外と定義し、falseを0と定義します(そして多くの言語は特定のブールデータ型を持つか整数のみをブール値として扱うことができます...他の型は変換を必要とします。PerlはTrue Fakeを解釈するのに非常に柔軟です。

シェルは、プログラムが正常に終了したか(0)またはエラーコード(プログラムによって1から127まで、それぞれ異なるエラー条件を表す)で終了したかどうかを確認する必要があるため、とにかく反対方向に実行します。実際にはtrue対falseではなく、成功対エラーコードです。外部プログラムから返された終了コードをテストする必要がある他のプログラムは、言語自体のtrue / false内部定義に関係なく同じことを行う必要があります。

注2:シェルループでPerl(または外部プログラム)を繰り返し実行することはお勧めできません。高速で現代的なシステムでは、開始オーバーヘッドはほんの数ミリ秒しかかかりませんが、これは数百または数千回のループ反復を引き起こし、非常に遅くなります。必ずシェルループでこれを行う必要がある場合は、他の方法を見つけるのが最善です。それ以外の場合は、ループ内でテストする必要があり、外部プログラムを繰り返し分岐できない場合は、スクリプト全体(または時間に敏感な部分)をPerl、awk、Python、またはシェル以外の他のスクリプトで書き換えることをお勧めします。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?このトピックに関する追加情報。

関連情報