grep에 공백이 포함된 여러 포함을 전달합니다.

grep에 공백이 포함된 여러 포함을 전달합니다.

전자 Get-ChildItemSelect-String후자 의 사용을 표준화하기 위해 PowerShell 및 Bash 스크립트를 만들고 있습니다 grep.

Bash 스크립트의 일부로 명령줄 인수를 사용하여 (를 포함하여 쉼표로 구분된 파일 이름 값을 구문 분석합니다.복수형) 이를 Grep에게 전달하려고 시도했지만 --include=여러 가지 어려움에 부딪혔습니다.

처음에는 중괄호 확장을 사용해 보았지만 (1) 제대로 작동하지 않았고 (2) grep이 기술적으로 이것을 지원하지 않으며 올바른 해결책은 다음을 사용하는 것이라고 읽었기 때문에 포기했습니다. 어쨌든 여러 개가 포함됩니다.

이제 성공적으로 작업한 여러 포함을 사용하려고 시도하고 있지만 값에 공백이 포함되지 않은 경우에만 해당 값이 인용되지 않았기 때문에 스크립트가 아무 작업도 수행하지 않습니다 $grepstring. 쉘 복사 및 붙여넣기 출력은 제대로 작동하지만 인용된 버전이 작동하도록 할 수 없습니다.

다음은 스크립트의 단순화된 버전입니다.

#!/bin/bash

include="$1"

if [[ $include == *","* ]]; then
    IFS=',' read -r -a includearray <<< "$include"
    
    includemulti=""
    
    firstloop="yes"
    
    for element in "${includearray[@]}"
    do
        # Trim leading and trailing whitespace
        element="${element## }"
        element="${element%% }"
        
        if [[ "$firstloop" == "yes" ]]; then
            firstloop="no"
            includemulti+="--include=$element"
            # includemulti+="--include=\"$element\""
            # includemulti+="--include='"$element"'"
            # includemulti+='--include="'$element'"'
            # includemulti+='--include="'"$element"'"'
            # includemulti+="--include='$element'"
        else
            includemulti+=" --include=$element"
            # includemulti+=" --include=\"$element\""
            # includemulti+=" --include='"$element"'"
            # includemulti+=' --include="'$element'"'
            # includemulti+=' --include="'"$element"'"'
            # includemulti+=" --include='$element'"
        fi
    done
    
    grep -ERins $includemulti "<pattern>" "<path>"
    
    grepstring="grep -ERins $includemulti \"<pattern>\" \"<path>\""
    echo $grepstring
else
    grep -ERins --include="$include" "<pattern>" "<path>"
fi

효율적인:

bash ~/test.sh 'Hello*.txt, *.sh'

bash ~/test.sh 'Hello W*.txt'

작동하지 않습니다:

bash ~/test.sh 'Hello W*.txt, *.sh'

여러 번 호출하는 것이 더 쉬울지 궁금해지기 시작했습니다. grep각 호출에는 다음이 포함됩니다.

답변1

분석하다

입력이 다음과 같으면 'Hello W*.txt, *.sh'공백이 구분 기호로 사용됩니다. 그래서 당신은 includemulti세 단어로 나누어 질 것입니다 :

  • --include=Hello
  • W*.txt
  • --include=*.sh

set -x명령 앞에 스크립트를 추가 하면 스크립트 grep가 어떻게 실행되는지 정확히 볼 수 있고 내가 말한 내용을 확인할 수 있습니다.

+ grep -ERins --include=Hello 'W*.txt' '--include=*.sh' <pattern> <path>

줄을 변경 includemulti+=하고 요소 주위에 따옴표를 추가하더라도 다음과 같습니다.

includemulti+=" --include=\"$element\""

bash공백은 여전히 ​​단어 구분 기호로 사용되므로 도움이 되지 않습니다 .

+ grep -ERins '--include="Hello' 'W*.txt"' '--include="*.sh"' <pattern> <path>

솔루션 1

스크립트를 덜 변경해야 하는 한 가지 가능한 솔루션은 요소 주위에 따옴표를 추가하는 것입니다.그리고eval명령 앞에 내장 명령을 추가합니다 grep. bash매뉴얼 페이지 에서 :

평가하다[아르기닌...]

이것매개변수읽어서 함께 연결하여 명령을 형성합니다. 그런 다음 명령은 셸에서 읽고 실행되며 종료 상태는 다음과 같이 반환됩니다.평가하다. 만약 없다면매개변수, 또는 그냥 빈 매개변수,평가하다0을 반환합니다.

eval따라서 grep 명령 앞에 추가 하면 실제로 다음을 실행하는 것과 같습니다.

bash -c 'grep -ERins --include="Hello W*.txt" --include="*.sh" <pattern> <path>'

set -x명령 앞에는 grep두 줄이 표시되며, 두 번째 줄은 실제로 실행되는 줄입니다.

+ eval grep -ERins '--include="Hello' 'W*.txt"' '--include="*.sh"' <pattern> <path>
++ grep -ERins '--include=Hello W*.txt' '--include=*.sh' <pattern> <path>

솔루션 2

이것은 더 우아한 솔루션입니다. 반복하는 대신 배열 변수를 수정할 수 있습니다 includearray.

# Remove leading space from every element in the array
includearray=("${includearray[@]## }")
# Remove trailing space from every element in the array
includearray=("${includearray[@]%% }")
# Add --include= as the prefix of every element in the array                                                                                                                
includearray=("${includearray[@]/#/--include=}")

그러면 grep명령은 다음과 같습니다.

grep -ERins "${includearray[@]}" <pattern> <path>

includearray이렇게 하면 배열의 각 요소가 공백 수에 관계없이 단일 단어로 처리되므로 요소를 따옴표로 묶을 필요가 없습니다 .

최종 코드는 다음과 같습니다.

#!/bin/bash

include="$1"

if [[ $include == *","* ]]; then
    IFS=',' read -r -a includearray <<< "$include"

    # Remove leading space from every element in the array
    includearray=("${includearray[@]## }")
    # Remove trailing space from every element in the array
    includearray=("${includearray[@]%% }")
    # Add --include= as the prefix of every element in the array                                                                                                                
    includearray=("${includearray[@]/#/--include=}")
    
    grep -ERins "${includearray[@]}" "<pattern>" "<path>"
else
    grep -ERins --include="$include" "<pattern>" "<path>"
fi

답변2

eval문자열을 작성하고 적용하는 대신 배열을 사용하여 매개변수 세트를 작성할 수 있습니다. 이는 따옴표, 공백 및 기타 문제로 인해 예기치 않은 오류가 발생하기 쉽습니다 grep.

#!/bin/bash
#
includesList="$1"
IFS=, read -ra includes <<<"$includesList"
# echo "! includesList=$includesList, includes=(${includes[@]}) !" >&2
[ 0 -eq "${#includes[@]}" ] && { echo "ERROR: Missing includes" >&2; exit 1; }

args=()
for include in "${includes[@]}"
do
    include="${include## }"
    include="${include%% }"
    args+=('--include' "$include")
done
# echo "! args=(${args[@]}) !" >&2

grep -EIins "${args[@]}" "<REpattern>" "<path>"

용법:

chmod a+rx code

./code 'trick*'
./code 'trick*,house,truck*.sh'
./code 'Hello W*.txt, *.sh'

이 두 디버깅 문의 주석을 제거하면 echo처음에는 누락된 따옴표로 인해 혼란스러울 수 있습니다. 값 자체에는 이를 표시하는 따옴표가 포함되어 있지 않으므로 마지막 예에서는 다음과 같은 결과를 얻게 됩니다.

! includesList=Hello W*.txt, *.sh, includes=(Hello W*.txt  *.sh) !
! args=(--include Hello W*.txt --include *.sh) !

이는 단순한 디버깅 명령문이기 때문에 어떤 값이 어떤 값인지 시각적으로 확인할 방법이 없습니다 args(). 실제로 --includex2 및 Hello W*.txtx2 4개의 배열 요소가 있습니다 *.sh. 보다 정교한 "배열 인쇄" 루틴은 메서드를 사용하여 printf '%q'적절하게 인용된 값을 출력할 수 있지만 여기서는 너무 지나친 것 같습니다.

{ printf '! args=('; printf "'%q' " "${args[@]}"; printf ') !\n'; } >&2

관련 정보