bash: 반복 문자가 작동하지 않습니다

bash: 반복 문자가 작동하지 않습니다

문자열에 공백과 해시 문자("#")만 포함되어 있는지 확인하는 이 코드가 있습니다. 그렇다면 "예"를 에코하고 그렇지 않으면 "아니오"를 에코합니다.

string="###############
        #             #
        # #############
        #             #
        # ######### # #
        #         # # #
        # ### ##### # #
        #   #     # # #
        # # ###########
        # #            
        ###############"
                       
 confirm_variable="Yes"
 for (( i=0; i<${#string}; i++ )); do
    str="${string:$i:1}"
    if [ "$str" == "#" ] || [ "$str" == " " ] || [ "$str" == "\n" ] ; 
        then
            continue
        else
            confirm_variable="No"
            break
    fi

done
echo $confirm_variable

왜 이것이 작동하지 않는지 잘 모르겠습니다. 문자열을 다음과 같게 만드는 것과 같습니다.

string="## #### # #  #" 

아주 잘 작동하는 것 같습니다.

답변1

"\n"두 문자 백슬래시와 소문자 n입니다.

이제 일부 쉘에서는 echo "\n"개행 문자(실제로는 2개)를 인쇄하고 모든 쉘에서는 을 인쇄합니다 printf "\n". 하지만 이는 백슬래시가 특별히 취급되기 때문입니다 echo. printf이는 백슬래시 이스케이프가 항상 (큰따옴표로 묶인) 문자열에서 특별히 처리되는 C, Perl 및 Python과 같은 언어와 다릅니다.

많은 쉘에서 $'\n'이것은 개행 문자만 포함하는 문자열일 것입니다. (그러나 순수 POSIX sh에서는 따옴표 안에 리터럴 개행 문자를 추가해야 합니다.)

즉, Bash/Ksh/Zsh에서는 수동으로 반복하는 대신 정규식에 대해 문자열을 테스트할 수 있습니다.

re=$'^[# \n]*$'
if [[ $string =~ $re ]]; then
    echo "string contains only #, space and newline"
fi

( for (( .. ))또한 ${string:i:j}이는 표준 POSIX 기능이 아니며 Ubuntu에 있는 Dash 등에서는 작동하지 않습니다 /bin/sh.)

답변2

다른 사람들이 지적했듯이 "\n"개행 문자가 아니라 \a 및 문자열이 있습니다 n.

문자열에 공백, 해시 및 줄바꿈만 포함되어 있는지 검색하려면 각 개별 문자를 반복하는 대신 패턴 일치를 사용하세요.

다음을 수행할 수 있습니다.

case $string in
    (*[![:space:]#]*)
        echo 'other characters in string'
        ;;
    (*)
        echo 'only space-like characters or hashes in string'
esac

또는 다음과 같습니다( bash).

if [[ $string == *[![:space:]#]* ]]; then
        echo 'other characters in string'
else
        echo 'only space-like characters or hashes in string'
fi

여기서 사용하는 것은 [:space:]공백, 탭(다양한 유형), 캐리지 리턴 및 줄 바꿈을 포함하여 다양한 공백 유사 문자와 일치하는 POSIX 문자 클래스입니다. 이 패턴은 *[![:space:]#]*다음 문자를 포함하는 모든 문자열과 일치합니다.아니요공백 같은 문자 또는 #.

탭이나 캐리지 리턴을 허용하지 않는 등 좀 더 제한적으로 적용하려면 $'*[! \n#]*'다음 패턴을 사용하세요 bash.

pattern=$'*[! \n#]*'
if [[ $string == $pattern ]]; then
        echo 'other characters in string'
else
        echo 'only spaces, hashes, or newlines in string'
fi

또는 표준 sh셸에서 다음을 수행합니다.

pattern='*[!
 #]*'

case $string in
    ($pattern)
        echo 'other characters in string'
        ;;
    (*)
        echo 'only spaces, hashes, or newlines in string'
esac

답변3

가장 사소한 문자열이나 텍스트 조작 이외의 작업을 수행하기 위해 sh(일반 sh에서 bash, ksh 또는 zsh까지의 모든 변형)를 사용해서는 안 됩니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?몇 가지 이유.

여러 줄 문자열의 각 문자에 대한 반복은 셸에서 개별적으로 수행되어서는 안 됩니다. 속도가 매우 느리고 모든 종류의 인용 및 줄 끝 표시(예: \n)에 문제가 있으며 대부분의 sh 버전(bash, ksh 및 zsh 제외)에는 내장 기능도 없습니다. 정규식 지원 - 제 생각에는 이것이 텍스트 처리에 필요한 최소한의 기능입니다.

대신 다른 텍스트 처리 유틸리티/언어를 awk사용 하십시오.perl

예를 들어 전체 for 루프를 다음으로 바꿀 수 있습니다.

echo "$string" | perl -lne 'BEGIN {$c="Yes"; $ec=0};
                            if (!m/^[ #]+$/) { $c="No"; $ec=1; last };
                            END {print $c; exit $ec}'

또는

echo "$string" | awk -v c="Yes" -v ec=0 \
                     '!/^[ #]*$/ { c="No"; ec=1; nextfile };
                      END { print c; exit ec}'

이를 위해서는 GNU awk (또는 기타 지원되는 awk nextfile)가 필요합니다. 다른 버전의 awk에서는 다음 코드가 작동하지만 일치 오류 시 루프를 즉시 종료하지 않기 때문에 약간 느립니다.

echo "$string" | awk -v c="Yes" -v ec=0 \
                     '!/^[ #]*$/ { c="No"; ec=1 };
                      END { print c; exit ec}'

분명히 이들은 동일한 스크립트이며 다른 Perl 및 awk 구문에 대해 약간 다르게 다시 작성되었습니다.

위의 정규식은 perl과 awk 모두에서 사용됩니다 /^[ #]*$/. !이는 공백이나 해시만 포함하지 않는 모든 줄과 일치합니다.

세 가지 모두 올바른 입력이 확인되면 종료 코드 0을 반환하고 일치하는 항목이 없으면 1을 반환합니다. 이는 sh 의 변수를 사용하여 $?테스트 할 수 있습니다.

if [ $? -eq 1 ] ; then
   : # string has bad characters, do something!
fi

간단한 GNU 스크립트를 사용할 수도 있습니다 sed.

echo "$string" | sed -n -e '/[^ #]/q1'
if [ $? -eq 0 ] ; then echo "Yes" else echo "No" ; fi

(quit) 명령의 종료 코드 q는 sed의 GNU 확장이므로 GNU sed가 필요합니다.

또는 @Isaac이 지적했듯이 grep's -q(또는 --quiet/ --silent) 옵션을 사용할 수 있습니다. 그러면 일반 출력이 억제되고 종료 코드만 반환됩니다. 예를 들어:

echo "$string" | grep -q '[^ #]'
if [ $? -eq 0 ] ; then echo "Yes" else echo "No" ; fi

관련 정보