명령을 전달하는 방법?

명령을 전달하는 방법?

다음과 같은 간단한 스크립트가 있습니다.

  • 이메일이 특정 패턴과 일치하는지 확인
  • 이 경우 태그 목록에 태그를 추가하세요.
  • 종료하기 전에 태그 목록을 인쇄하십시오.
set -e

lista_tag=()
in_file="/tmp/grepmail-classify.txt"

# save stdin to file, to use it multiple times
cp /dev/stdin $in_file


# CLASSIFY

res=$(grepmail -B "some regex pattern" < $in_file)
if [ ! -z "$res" ]
then
    lista_tag+=("PUSH")
fi

res=$(grepmail -B "some other regex pattern" < $in_file)
if [ ! -z "$res" ]
then
    lista_tag+=("MERGIFY")
fi

# ⁝ Many many more similar patterns

# output them comma separated
echo ${lista_tag[*]}

보시다시피 리팩토링과 추상화의 경우가 있습니다. res그리고 if .. fi부분적으로 반복되었습니다. 하지만 명령을 안전하게 전달하는 방법을 잘 모르겠습니다. 제가 하고 싶은 것은 다음과 같은(또는 유사한) 함수를 호출하는 것입니다.

classify '"grepmail -B "somepattern"' 'MYTAG'

하지만 까다 롭습니다! 내가 읽고자주하는 질문하지만 내 경우에는 작동하는지 잘 모르겠습니다.

따라서 질문은 다음과 같습니다. 명령을 전달하는 올바른 방법은 무엇입니까(있는 경우)? res=그러한 기능의 일부는 어떤 모습일까요?

답변1

classify '"grepmail -B "somepattern"' 'MYTAG'

에서 언급한 바로 그 이유 때문에 작업을 시작하기가 어렵습니다.배쉬 FAQ 050.

그러나 "tag" 매개변수를 앞에 놓으면 명령의 나머지 부분을 사용할 수 있으므로 작동하게 만들 수 있습니다.

#!/bin/bash
lista_tag=()
classify() {
    local tag="$1"
    shift
    res=$( "$@" < "$in_file")
    if [ -n "$res" ]; then
        lista_tag+=("$tag")
    fi
}
classify PUSH    grepmail -B "some regex pattern"
classify MERGIFY grepmail -B "some other regex pattern"

여기서 핵심은 바로 우리다아니요명령의 인수를 하나의 문자열에 붙여넣되 별도로 유지하십시오. "$@"그것은 마술입니다. 모든 위치 매개변수로 개별적으로 확장됩니다. 태그를 제거하고 나면 남은 것은 명령뿐입니다.

그러나 명령을 실행하고 적절하게 인용해야 하기 때문에 같은 방식으로 리디렉션을 붙여넣을 수는 없습니다 eval. 또한 사용자가 제공한 입력에 대해 이 작업을 주의 깊게 수행해야 합니다. 그렇지 않으면 명령 실행 취약점이 남을 가능성이 높습니다.

어쨌든 해당 grepmail -B부분은 상수인 것 같으므로 레이블과 모드를 전달하면 됩니다.

#!/bin/bash
lista_tag=()
in_file=foo.txt
classify() {
    local pattern="$1"
    local tag="$2"
    if [[ -n "$(grepmail -B "$pattern" < "$in_file")" ]]; then
        lista_tag+=("$tag")
    fi
}
classify "some regex pattern" PUSH
classify "some other regex pattern" MERGIFY

답변2

이는 연관 배열을 사용할 수 있는 좋은 기회입니다.

#!/bin/bash
lista_tag=()

declare -A patterns=(
    [PUSH]='some regex pattern'
    [MERGIFY]='some other pattern'
    # ... other [tag]=pattern pairs ...
)

# capture stdin
in_file=$(mktemp)
cat > $in_file

# CLASSIFY

# iterate over the array indices
for tag in "${!patterns[@]}"; do
    if [[ -n "$( grepmail -B "${patterns[$tag]}" < "$in_file" )" ]]; then
        lista_tag+=("$tag")
    fi
done

# print comma-separated list
( IFS=","; echo "${lista_tag[*]}" )

grepmail일치하는 항목이 없는 경우(예:) 0이 아닌 종료 상태를 가질 수 있으면 grep -q더 간단합니다 .

for tag in "${!patterns[@]}"; do
    grepmail -q -B "${patterns[$tag]}" < "$in_file" && lista_tag+=("$tag")
done

관련 정보