Bash 함수는 어떻게 여러 값을 반환합니까?

Bash 함수는 어떻게 여러 값을 반환합니까?

반품에 대한 모범 사례는 무엇입니까?많은bash 기능의 가치?

예시 1:

함수 스크립트:

function mysqlquery {
    local dbserver='localhost'
    local dbuser='user'
    local dbpass='pass'
    local db='mydb'
    mysql -h "$dbserver" -u "$dbuser" -p "$dbpass" --skip-column-names --raw -e "$*" "$db"
    if [ $? -ne 0 ]; then
        return 1
    fi
}

소스 스크립트:

for XY in $(mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null);do
    dosomethingwith $XY
done
if mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null; then
    echo true
fi

예 2:

함수 스크립트:

function mysqlquery {
    local dbserver='localhost'
    local dbuser='user'
    local dbpass='pass'
    local db='mydb'
    result=$(mysql -h "$dbserver" -u "$dbuser" -p "$dbpass" -e "$*" "$db" 2>/dev/null)
    if [ $? -ne 0 -o -z "$result" ]; then
        return 1
    fi
}

소스 스크립트:

result=$(mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null)
for XY in $result;do
    dosomethingwith $XY
done
if mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null; then
    echo true
fi

아니면 여러 정보(단일 int 값보다 훨씬 더 많은 정보)를 반환하는 더 많은 방법이 있습니까?

답변1

예, 숫자만 반환 bash될 수 있으며 return0에서 255 사이의 정수만 반환될 수 있습니다.

무엇이든(사물 목록) 반환할 수 있는 셸의 경우 다음을 볼 수 있습니다 es.

$ es -c "fn f {return (a 'b c' d \$*)}; printf '%s\n' <={f x y}"
a
b c
d
x
y

이제 Korn과 같은 셸에서는 bash언제든지 미리 합의된 변수로 데이터를 반환할 수 있습니다. 변수는 셸에서 지원하는 모든 유형이 될 수 있습니다.

의 경우 bash스칼라 희소 배열(양의 정수로 제한된 키가 있는 연관 배열) 또는 null이 아닌 키가 있는 연관 배열(키나 값 모두 NUL 문자를 포함할 수 없음)일 수 있습니다.

zsh이러한 제한이 없는 일반 배열 및 연관 배열 도 참조하세요 .

위의 기능과 동등한 기능은 다음을 f es통해 수행할 수 있습니다.

f() {
  reply=(a 'b c' d "$@")
}
f
printf '%s\n' "${reply[@]}"

이제 mysql쿼리는 일반적으로 2D 배열인 테이블을 반환합니다. 내가 아는 한, 다차원 배열이 있는 유일한 쉘은 ksh93( bash비록 변수에서 NUL 문자를 지원하지는 않지만) 입니다.

ksh또한 지원합니다화합물제목이 있는 테이블을 편리하게 반환하는 변수입니다.

또한 참조로 변수 전달을 지원합니다.

따라서 다음과 같이 할 수 있습니다.

function f {
  typeset -n var=$1
  var=(
    (foo bar baz)
    (1 2 3)
  }
}
f reply
printf '%s\n' "${reply[0][1]}" "${reply[1][2]}"

또는:

function f {
  typeset -n var=$1
  var=(
    (firstname=John lastname=Smith)
    (firstname=Alice lastname=Doe)
  )
}

f reply
printf '%s\n' "${reply[0].lastname}"

이제 출력을 얻고 이를 일부 변수에 저장하려면 mysql테이블의 열이 TAB 문자로 구분되고 행이 NL로 구분된 텍스트인 출력을 구문 분석하고 값을 일부 인코딩해야 합니다. 허용 NL과 탭을 모두 포함합니다.

그렇지 않은 경우 --rawNL mysqlas \n, TAB as \t, 백슬래시 as \\및 NUL as 가 출력됩니다 \0.

ksh93read -C변수 정의로 형식화된 텍스트를 읽는 것도 가능하므로( 사용 eval하는 것과 크게 다르지는 않지만) 다음과 같이 할 수 있습니다.

function mysql_to_narray {
  awk -F '\t' -v q="'" '
    function quote(s) {
      gsub(/\\n/, "\n", s)
      gsub(/\\t/, "\t", s)
      gsub(/\\\\/, "\\", s)
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN{print "("}
    {
      print "("
      for (i = 1; i <= NF; i++)
        print " " quote($i)
      print ")"
    }
    END {print ")"}'
}

function query {
  typeset -n var=$1
  typeset db=$2
  shift 2

  typeset -i n=0
  typeset IFS=' '
  typeset credentials=/path/to/file.my # not password on the command line!
  set -o pipefail

  mysql --defaults-extra-file="$credentials" --batch \
        --skip-column-names -e "$*" "$db" |
    mysql_to_narray |
    read -C var
}

다음과 같이 사용됨

query myvar mydb 'select * from mytable' || exit
printf '%s\n' "${myvar[0][0]}"...

또는 복합 변수의 경우:

function mysql_to_array_of_compounds {
  awk -F '\t' -v q="'" '
    function quote(s) {
      gsub(/\\n/, "\n", s)
      gsub(/\\t/, "\t", s)
      gsub(/\\\\/, "\\", s)
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN{print "("}
    NR == 1 {
      for (i = 1; i<= NF; i++) header[i] = $i
      next
    }
    {
      print "("
      for (i = 1; i <= NF; i++)
        print " " header[i] "=" quote($i)
      print ")"
    }
    END {print ")"}'
}

function query {
  typeset -n var=$1
  typeset db=$2
  shift 2

  typeset -i n=0
  typeset IFS=' '
  typeset credentials=/path/to/file.my # not password on the command line!
  set -o pipefail

  mysql --defaults-extra-file="$credentials" --batch \
        -e "$*" "$db" |
    mysql_to_array_of_compounds |
    read -C var
}

다음과 같이 사용됩니다:

query myvar mydb 'select "First Name" as firstname, 
                         "Last Name" as lastname from mytable' || exit

printf '%s\n' "${myvar[0].firstname"

헤더 이름( firstnamelastname)은 유효한 쉘 식별자여야 합니다.

또는 (zsh 및 yash 배열 인덱스는 1에서 시작하고 NUL 문자만 저장할 수 있지만 ) 열당 하나의 배열을 반환하는 코드를 생성하여 항상 정의 bashzsh수 있습니다 .yashzshawk

query() {
  typeset db="$1"
  shift

  typeset IFS=' '
  typeset credentials=/path/to/file.my # not password on the command line!
  set -o pipefail

  typeset output
  output=$(
    mysql --defaults-extra-file="$credentials" --batch \
          -e "$*" "$db" |
      awk -F '\t' -v q="'" '
        function quote(s) {
          gsub(/\\n/, "\n", s)
          gsub(/\\t/, "\t", s)
          gsub(/\\\\/, "\\", s)
          gsub(q, q "\\" q q, s)
          return q s q
        }
        NR == 1 {
          for (n = 1; n<= NF; n++) column[n] = $n "=("
          next
        }
        {
          for (i = 1; i < n; i++)
            column[i] = column[i] " " quote($i)
        }
        END {
          for (i = 1; i < n; i++)
            print column[i] ") "
        }'
  ) || return
  eval "$output"
}

다음과 같이 사용됩니다:

query mydb 'select "First Name" as firstname, 
                         "Last Name" as lastname from mytable' || exit

printf '%s\n' "${firstname[1]}"

메소드와 마찬가지로 옵션을 로컬 함수로 설정하기 전에 bash4.4+를 set -o localoptions사용하거나 사용하여 추가하세요.zshlocal -set -o pipefailksh93

위의 모든 항목에서는 or가 차단하기 \0때문에 s를 실제 NUL로 다시 변환 하지 않는다는 점에 유의하세요 . BLOB를 사용하고 싶다면 이 작업을 수행할 수 있지만 모든 구현 에서 작동하지는 않는다는 점에 유의하세요.bashksh93zshgsub(/\\0/, "\0", s)awk

어쨌든, 여기서는 이런 종류의 작업을 수행하기 위해 쉘보다 더 높은 수준의 언어(예: Perl 또는 Python)를 사용합니다.

답변2

글쎄요, 원하는/필요한 출력 형식에 따라 다릅니다. 가장 쉬운 방법은 아마도 함수의 출력을 인쇄하여 함수가 다른 명령처럼 동작하도록 하는 것입니다. 또 다른 방법은 함수 내부에서 일부 변수(아마도 연관 배열)를 설정하는 것입니다. 이것의 장점은 서로 다른 프로젝트를 깔끔하게 분리할 수 있지만 일부 변수를 하드코딩해야 할 수도 있다는 것입니다.

첫 번째 예제의 함수는 전자를 수행합니다. mysql 클라이언트가 함수에서 인쇄하는 것은 무엇이든 함수의 표준 출력으로 이동합니다. 데이터는 이미 바이트 스트림 형태이므로 그대로 놔두시면 됩니다.

하지만 여기서 문제는 출력을 어떻게 처리할 것인지가 됩니다. for x in $(somecmd) ...출력이 somecmd단어로 분할되어 파일 이름에 대해 전체적으로 처리되기 때문에 좋지 않습니다. 일반적으로 사용하는 것이 더 좋습니다 while read .... 참조파일(데이터 스트림, 변수)을 한 줄씩(및/또는 필드별로) 읽는 방법은 무엇입니까?

mysql출력을 한 줄씩 읽으려면 다음을 수행하십시오.

mysql -h "$dbserver" etc. etc. | while read -r line ; do
    dosomethingwith "$line"
done

아니면 함수를 사용하세요

mysqlquery() {
    ...
    mysql -h "$dbserver" etc. etc. 2>/dev/null
}
mysqlquery | while read -r line ; do ...

if [ $? -ne 0 ]; then return 1함수의 반환 값은 마지막 명령의 반환 값과 동일할 필요는 없습니다 . 파이프라인에 공급하기 위해 함수를 사용하는 경우 반환 값을 확인하기가 쉽지 않습니다.

관련 정보