【问题标题】:How to split one string into multiple variables in bash shell? [duplicate]如何在 bash shell 中将一个字符串拆分为多个变量? [复制]
【发布时间】:2012-05-18 05:39:45
【问题描述】:

我一直在寻找解决方案并发现了类似的问题,只是他们试图用空格分隔句子,而答案对我的情况不起作用。

目前正在将一个变量设置为这样的字符串:
ABCDE-123456
我想把它分成2个变量,同时消除“-”。即:
var1=ABCDE
var2=123456

怎么可能做到这一点?


这是对我有用的解决方案:
var1=$(echo $STR | cut -f1 -d-)
var2=$(echo $STR | cut -f2 -d-)

是否可以使用不带分隔符的 cut 命令(每个字符都设置为变量)?

var1=$(echo $STR | cut -f1 -d?)
var2=$(echo $STR | cut -f1 -d?)
var3=$(echo $STR | cut -f1 -d?)
etc.

【问题讨论】:

  • 对于您的第二个问题,请参阅下面的@mkb 对我的回答的评论 - 这绝对是要走的路!
  • 查看我编辑的答案,了解将单个字符读入数组的一种方法。
  • 这里是同样的东西,形式更简洁:var1=$(cut -f1 -d-

标签: bash shell variables


【解决方案1】:

要拆分以- 分隔的字符串,可以使用readIFS

$ IFS=- read var1 var2 <<< ABCDE-123456
$ echo "$var1"
ABCDE
$ echo "$var2"
123456

编辑:

以下是如何将每个单独的字符读入数组元素:

$ read -a foo <<<"$(echo "ABCDE-123456" | sed 's/./& /g')"

转储数组:

$ declare -p foo
declare -a foo='([0]="A" [1]="B" [2]="C" [3]="D" [4]="E" [5]="-" [6]="1" [7]="2" [8]="3" [9]="4" [10]="5" [11]="6")'

如果字符串中有空格:

$ IFS=$'\v' read -a foo <<<"$(echo "ABCDE 123456" | sed 's/./&\v/g')"
$ declare -p foo
declare -a foo='([0]="A" [1]="B" [2]="C" [3]="D" [4]="E" [5]=" " [6]="1" [7]="2" [8]="3" [9]="4" [10]="5" [11]="6")'

【讨论】:

  • 太棒了,优雅的 bash-only 方式,没有不必要的分叉。
  • 此解决方案还有一个好处是,如果分隔符不存在,var2 将为空
  • read 在带有输入重定向的循环内不起作用。 read 会选择一个错误的文件描述符来读取。
  • 我最初给这个答案一个加分作为一个优雅的解决方案,但现在发现它在 Bash v3 和 v4 上的工作方式不同,因此不适用于预安装 bash v3 的 macos。不幸的是,由于投票被锁定,我现在不能对答案投反对票:(
  • 一种更通用、更正确的方式:IFS=- read -r -d '' var1 var2 &lt; &lt;(printf %s "ABCDE-123456")-r -d ''&lt;(printf %s ...) 很重要
【解决方案2】:

如果您知道它将只有两个字段,则可以使用以下方法跳过额外的子流程:

var1=${STR%-*}
var2=${STR#*-}

这是做什么的? ${STR%-*} 删除 $STR 中与模式 -* 从字符串末尾开始匹配的最短子字符串。 ${STR#*-} 做同样的事情,但使用 *- 模式并从字符串的开头开始。他们每个都有对应的 %%## 找到 longest 锚定模式匹配。如果有人有一个有用的助记符来记住哪个做什么,请告诉我!我总是必须尝试两者来记住。

请参阅bash documentation 了解更多信息。

【讨论】:

  • Plus 1 用于了解您的 POSIX shell 功能、避免昂贵的分叉和管道以及不存在 bashism。
  • Dunno 关于“不存在 bashisms”,考虑到这已经有点神秘......如果你的分隔符是换行符而不是连字符,那么它会变得更加神秘。另一方面,它适用于换行符,所以就是这样。
  • 对其实际工作原理的描述会有所帮助
  • 我终于找到了它的文档:Shell-Parameter-Expansion
  • 助记符:“#”在标准键盘上“%”的左边,所以“#”去掉前缀(左边),“%”去掉后缀(右边)。
【解决方案3】:

如果您的解决方案不必是通用的,即只需要像您的示例那样适用于字符串,您可以这样做:

var1=$(echo $STR | cut -f1 -d-)
var2=$(echo $STR | cut -f2 -d-)

我在这里选择了cut,因为您可以简单地为更多变量扩展代码...

【讨论】:

  • 你能再看看我的帖子,看看你是否有解决后续问题的方法?谢谢!
  • 你也可以使用cut来剪切字符!以cut -c1 为例。
  • 虽然这很容易读写,但这是一个非常慢的解决方案,因为它会强制你读取两次相同的数据 ($STR) ...如果你关心你的脚本性能,@anubhava解决方案要好得多
  • 除了是一个丑陋的最后解决方案之外,这还有一个错误:您绝对应该在 echo "$STR" 中使用双引号,除非您特别希望 shell 扩展字符串中的任何通配符作为副作用.另见stackoverflow.com/questions/10067266/…
  • 你对双引号当然是对的,尽管我确实指出这个解决方案并不通用。但是,我认为您的评估有点不公平-对于某些人来说,此解决方案可能比其他解决方案更具可读性(因此可扩展等),并且不完全依赖无法转换为其他外壳的神秘 bash 功能。我怀疑这就是为什么我的解决方案虽然不那么优雅,但会继续定期获得投票......
【解决方案4】:

听起来像是 set 的工作,带有自定义 IFS

IFS=-
set $STR
var1=$1
var2=$2

(您将希望在带有local IFS 的函数中执行此操作,这样您就不会弄乱脚本的其他部分,因为您需要IFS 是您所期望的。)

【讨论】:

  • 很好 - 我知道$IFS,但还没有看到如何使用它。
  • 我使用了 Triplee 的示例,它的工作原理与宣传的完全一样!如果您需要将它们存储在带有多个“抛出”变量的脚本中,只需将最后两行更改为
     myvar1=echo $1 && myvar2=echo $2 
  • 如果我们需要编写非 Bash 特定的东西,这是一个非常好的解决方案。要处理IFS的问题,可以在覆盖之前在开头添加OLDIFS=$IFS,然后在set之后添加IFS=$OLDIFS
  • 可能在set -- $STR 之前添加set -f 以禁用路径名扩展,或者如果$STR 包含模式,它将捕获路径文件名。
【解决方案5】:

使用 bash 正则表达式功能:

re="^([^-]+)-(.*)$"
[[ "ABCDE-123456" =~ $re ]] && var1="${BASH_REMATCH[1]}" && var2="${BASH_REMATCH[2]}"
echo $var1
echo $var2

输出

ABCDE
123456

【讨论】:

  • 喜欢预先定义 re 以供以后使用!
猜你喜欢
  • 2014-07-03
  • 2010-11-30
  • 2017-03-24
  • 1970-01-01
  • 2018-01-31
  • 2015-12-19
  • 2021-12-11
  • 1970-01-01
相关资源
最近更新 更多