"test"의 두 표현을 XOR하는 방법은 무엇입니까?

"test"의 두 표현을 XOR하는 방법은 무엇입니까?

디렉터리( 이라고 칭함 )에 두 개의 파일( 및 dir이라고 칭함 ) 중 하나가 포함되어 있지만 파일이 하나도 없고 둘 다 포함되어 있지 않은지 확인해야 합니다.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

이로 인해 [두 테스트에서 0이 아닌 다른 종료 코드가 생성될 수 있습니다.
사양에 따르면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하세요아니요오타. )

테스트 중 하나를 반복해야 한다는 점은 인정합니다. 따라서 반전을 제외하고는 동일하다는 점을 분명히 하기 위해 수직으로 정렬했습니다 !.

장점은 $?임시 변수나 이와 유사한 변수를 보관하는 데 따른 혼란을 피하고 (거의) 가장 성능이 좋다는 것입니다.

-a마지막으로 명령의 및 옵션이 문제가 있다는 점을 지적하고 싶습니다 . 구문 분석이 모호해져서 잘못된 결과가 발생할 수 있습니다. 또는로 구분된 두 개의 테스트 명령을 사용하면 됩니다.-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를 0이 아닌 것으로 정의하고 false를 0으로 정의합니다(그리고 많은 언어는 특정 부울 데이터 유형을 갖거나 정수만 부울로 처리하도록 허용합니다...다른 유형은 변환이 필요합니다. Perl은 True Fake를 해석하는 데 매우 유연합니다. ).

쉘은 프로그램이 성공적으로 종료되었는지(0) 또는 오류 코드(프로그램에 따라 1에서 127까지, 각각 다른 오류 조건을 나타냄)로 종료되었는지 대부분/종종 알아야 하기 때문에 어쨌든 반대 방향으로 수행합니다. 실제로는 true 대 false가 아니라 성공 대 오류 코드입니다. 외부 프로그램에서 반환된 종료 코드를 테스트해야 하는 다른 프로그램은 언어 자체의 true/false 내부 정의에 관계없이 동일한 작업을 수행해야 합니다.

참고 2: 쉘 루프에서 Perl(또는 외부 프로그램)을 반복적으로 실행하는 것은 권장되지 않습니다. 빠르고 현대적인 시스템에서는 시작 오버헤드가 몇 밀리초에 불과할 수 있지만 이로 인해 수백 또는 수천 번의 루프 반복이 발생하고 속도가 매우 느려집니다. 반드시 쉘 루프에서 이 작업을 수행해야 한다면 다른 방법을 찾는 것이 가장 좋습니다. 그렇지 않고 루프에서 테스트해야 하고 외부 프로그램을 반복적으로 분기할 수 없는 경우 전체 스크립트(또는 시간에 민감한 부분)를 Perl, awk, Python 또는 쉘이 아닌 기타 다른 스크립트로 다시 작성하는 것이 좋습니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?이 주제에 대한 추가 정보.

관련 정보