AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 408543
Accepted
FaxMax
FaxMax
Asked: 2017-12-03 16:12:16 +0800 CST2017-12-03 16:12:16 +0800 CST 2017-12-03 16:12:16 +0800 CST

bash 函数如何返回多个值?

  • 772

从 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 值)?

bash shell-script
  • 2 2 个回答
  • 38851 Views

2 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2017-12-04T10:27:48+08:002017-12-04T10:27:48+08:00

    是的,bash'sreturn只能返回数字,并且只能返回 0 到 255 之间的整数。

    对于可以返回任何内容(事物列表)的 shell,您可以查看es:

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

    现在,在类似 Korn 的 shell 中bash,您始终可以在预先约定的变量中返回数据。并且该变量可以是 shell 支持的任何类型。

    对于bash,可以是标量、稀疏数组(键限制为正整数的关联数组)或具有非空键的关联数组(键和值都不能包含 NUL 字符)。

    另请参阅zsh没有这些限制的普通数组和关联数组。

    上述功能的等价物f es可以通过以下方式完成:

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

    现在,mysql查询通常返回表,即二维数组。我知道的唯一具有多维数组的外壳是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和标签。

    没有--raw,mysql将输出 NL as \n, TAB as \t,反斜杠 as\\和 NUL as \0。

    ksh93也read -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"
    

    请注意,标头名称(firstname以上lastname)必须是有效的外壳标识符。

    在bashor zshoryash 中(尽管注意数组索引在 zsh 和 yash 中从 1 开始,并且只能zsh存储 NUL 字符),您总是可以通过awk生成代码来定义它们,每列返回一个数组:

    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 localoptionswithzsh或local -with bash4.4+,set -o pipefail以便将该选项的设置设置为函数的本地,就像使用该ksh93方法一样。

    请注意,在上述所有内容中,我们不会将\0s 转换回真正的 NUL,因为bash或者ksh93会阻塞它们。zsh如果使用能够与 BLOB 一起工作,您可能想要这样做,但请注意,gsub(/\\0/, "\0", s)这不适用于所有awk实现。

    无论如何,在这里,我会使用比 perl 或 python 之类的 shell 更高级的语言来做这种事情。

    • 13
  2. ilkkachu
    2017-12-04T08:18:13+08:002017-12-04T08:18:13+08:00

    好吧,这取决于您想要/需要什么样的输出格式。最简单的方法可能是只打印函数的输出,这样函数的行为就像任何其他命令一样。另一种方法是从函数内部设置一些变量(可能是关联数组)。这样做的好处是不同的项目被干净地分开,但您可能需要对一些变量进行硬编码。

    您的第一个示例中的函数实现了前者:mysql 客户端从函数打印的任何内容都会转到函数的标准输出。鉴于数据已经以字节流的形式出现,保持原样就可以了。

    虽然在这里,问题变成了如何处理输出。for x in $(somecmd) ...不好,因为 的输出somecmd被拆分为单词并针对文件名 glob 进行处理。通常使用更好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: 函数的返回值与最后一个命令的返回值相同。如果您使用该函数来馈送管道,那么查看返回值并不容易。

    • 1

相关问题

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • MySQL Select with function IN () with bash array

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    JSON数组使用jq来bash变量

    • 4 个回答
  • Marko Smith

    日期可以为 GMT 时区格式化当前时间吗?[复制]

    • 2 个回答
  • Marko Smith

    bash + 通过 bash 脚本从文件中读取变量和值

    • 4 个回答
  • Marko Smith

    如何复制目录并在同一命令中重命名它?

    • 4 个回答
  • Marko Smith

    ssh 连接。X11 连接因身份验证错误而被拒绝

    • 3 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Marko Smith

    systemctl 命令在 RHEL 6 中不起作用

    • 3 个回答
  • Marko Smith

    rsync 端口 22 和 873 使用

    • 2 个回答
  • Marko Smith

    以 100% 的利用率捕捉 /dev/loop -- 没有可用空间

    • 1 个回答
  • Marko Smith

    jq 打印子对象中所有的键和值

    • 2 个回答
  • Martin Hope
    EHerman JSON数组使用jq来bash变量 2017-12-31 14:50:58 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Drux 日期可以为 GMT 时区格式化当前时间吗?[复制] 2017-12-26 11:35:07 +0800 CST
  • Martin Hope
    AllisonC 如何复制目录并在同一命令中重命名它? 2017-12-22 05:28:06 +0800 CST
  • Martin Hope
    Steve “root”用户的文件权限如何工作? 2017-12-22 02:46:01 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST
  • Martin Hope
    Cbhihe 将默认编辑器更改为 vim for _ sudo systemctl edit [unit-file] _ 2017-12-03 10:11:38 +0800 CST
  • Martin Hope
    showkey 如何下载软件包而不是使用 apt-get 命令安装它? 2017-12-03 02:15:02 +0800 CST
  • Martin Hope
    youxiao 为什么目录 /home、/usr、/var 等都具有相同的 inode 编号 (2)? 2017-12-02 05:33:41 +0800 CST
  • Martin Hope
    user223600 gpg —list-keys 命令在将私钥导入全新安装后输出 uid [未知] 2017-11-26 18:26:02 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve