如何在Bash中分割一个字符串?

3

我有一个变量 x,其中包含类似于“2025-12-13-04-32 Hello StackOverflow程序员”的字符串。

我该如何将其拆分成以下几个变量:

d=2015
m=12
dy=13 
h=04
mi=32 
t="Hello StackOverflow programmers"
4个回答

10

一个read命令就可以完成:

input='2025-12-13-04-32 Hello stackoverflow programmers'

IFS='- ' read -r d m dy h mi t <<<"$input"

注意:<<<是所谓的here-string,它允许通过stdin提供一个常规的字符串[变量],与多行here-doc(以<<EOF开头)相关。在这里,它是比echo "$input" | IFS='- ' read ...更短、更高效的替代方法。

  • IFS='- '使read将输入行拆分为令牌,可以是空格或-
  • 即使通常也会将字符串'Hello stackoverflow programmers'分成3个令牌,但由于read将行的剩余部分分配给最后指定的变量,以防没有足够的变量来匹配结果的令牌;因此,变量$t接收'Hello stackoverflow programmers',正如所期望的那样。

打印结果,请使用以下代码:

请注意,${!name}变量间接引用的一个实例,即通过包含其名称的另一个变量访问变量。

names=( d m dy h mi t )
for name in "${names[@]}"; do
  printf '%s=%s\n' "$name" "${!name}"
done

这将产生:

d=2025
m=12
dy=13
h=04
mi=32
t=Hello stackoverflow programmers

关于方法的选择的说明:

  • read适用于基于一组文字分隔符进行的基于字段的解析(注意,每个非空格分隔符字符的每个实例都会被计算)。
    • 使用read -ra,您甚至可以将所有令牌读入一个数组中,而不必预先知道令牌数量。
  • 对于更灵活的解析(基于已知数量的令牌),请考虑使用正则表达式的=~,如 Jahid's answer所述。

1
好的,这是我的脚本问题,现在它已经正常工作了。谢谢。 - Craxxurz
1
Here-string 不完全等同于管道 echo blah | stuff,而更类似于 here-doc。除此之外的小细节问题,你的回答在 OP 的情况下绝对是最好的。 - gniourf_gniourf
@gniourf_gniourf。谢谢;我已经添加了对here-docs的引用,并将“equivalent”更改为“alternative”。 - mklement0

5

使用Bash正则表达式:

#!/bin/bash
s='2025-12-13-04-32 Hello stackoverflow programmers'
pat="([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)[[:space:]]*(.*)"
[[ $s =~ $pat ]]
echo "${BASH_REMATCH[1]}"
echo "${BASH_REMATCH[2]}"
echo "${BASH_REMATCH[3]}"
echo "${BASH_REMATCH[4]}"
echo "${BASH_REMATCH[5]}"
echo "${BASH_REMATCH[6]}"

输出:

2025
12
13
04
32
Hello stackoverflow programmers

对我来说,它没有显示“Hello stackoverflow程序员”。 - Craxxurz
1
好的,我的脚本出了问题,现在它已经正常工作了。谢谢。 我为 "$1" 中的每个文件执行了此操作:for x in $(ls $1) - Craxxurz
1
++ 可以通过 =~ 实现更灵活的解析; 您还可以按以下方式创建(并打印)各个变量:names=(d m dy h mi t); i=0; for (( i = 0; i < "${#names[@]}"; i++ )); do declare "${names[i]}=${BASH_REMATCH[i+1]}"; printf '%s=%s\n' "${names[i]}" "${!names[i]}"; done; @Craxxurz: 最好不要使用 for 解析命令输出。 (http://mywiki.wooledge.org/DontReadLinesWithFor) - mklement0

1
为了自动将以空格分隔的值分配给变量名,您可以这样做:
x="2025-12-13-04-32 Hello stackoverflow programmers"
read -r d m dy h mi <<< "$x"

但是由于字符串开头包含破折号(-),你需要先将其替换为空格:

x="2025-12-13-04-32 Hello stackoverflow programmers"
x="$(echo "$x" | sed 's/-/ /g')"
read -r d m dy h mi <<< "$x"

你还需要调整变量名的数量 ;-)

我认为这就是你想要的:

x="2025-12-13-04-32 Hello stackoverflow programmers"                                                                                        
x="$(echo "$x" | tr '-' ' ')"                                                                                                               
read -r d m dy h mi t <<< "$x"                                                                                                          
echo $d
2025
echo $m
12
echo $dy
13
echo $h 
04
echo $mi
32
echo $t
Hello stackoverflow programmers

2
你可以使用参数扩展来替换字符串中的字符,而不是使用外部的sed命令:x="${x//-/ }" - Benjamin W.

0
使用参数替换和declare
#!/bin/bash
x="2025-12-13-04-32 Hello stackoverflow programmers"

Shift () {
    declare -g "$1=${copy%%-*}"
    copy=${copy#*-}
}

copy=$x

for var in d m dy h mi ; do
    Shift $var
done

mi=${mi%% *} # Remove the message
t=${copy#* }

for var in d m dy h mi t ; do
    echo $var ${!var}
done

1
提醒那些被困在 Bash 4.1 或更低版本 的人:不支持 declare -g;没有 -gdeclare 在函数内部创建一个 局部 变量。 通常,简单地省略 declare 也会创建一个全局变量,但由于此处使用参数值作为变量 _名称_,因此需要使用 declare。 一个在(至少)Bash 3.x 中可用的替代方法是使用 printf -v 创建全局变量:printf -v "$1" %s "${copy%%-*}" - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接