我把这个字符串存储在一个变量中:
IN="bla@some.com;john@home.com"
现在我想通过;
分隔符拆分字符串,以便我有:
ADDR1="bla@some.com" ADDR2="john@home.com"
我不一定需要ADDR1
和ADDR2
变量.如果它们是阵列的元素甚至更好.
根据以下答案的建议,我最终得到了以下内容,这就是我所追求的:
#!/usr/bin/env bash IN="bla@some.com;john@home.com" mails=$(echo $IN | tr ";" "\n") for addr in $mails do echo "> [$addr]" done
输出:
> [bla@some.com] > [john@home.com]
有一个涉及设置Internal_field_separator(IFS)的解决方案;
.我不确定该答案发生了什么,你如何重置IFS
为默认值?
RE:IFS
解决方案,我试过这个并且它可以工作,我保持旧的IFS
然后恢复它:
IN="bla@some.com;john@home.com" OIFS=$IFS IFS=';' mails2=$IN for x in $mails2 do echo "> [$x]" done IFS=$OIFS
顺便说一句,我试过的时候
mails2=($IN)
我只是在循环打印时得到第一个字符串,没有括号围绕$IN
它工作.
您可以设置内部字段分隔符(IFS)变量,然后将其解析为数组.当在命令中发生这种情况时,分配IFS
仅发生在该单个命令的环境(to read
)中.然后它根据IFS
变量值将输入解析为一个数组,然后我们可以迭代它.
IFS=';' read -ra ADDR <<< "$IN" for i in "${ADDR[@]}"; do # process "$i" done
它将解析由一行分隔的项目;
,将其推入一个数组.处理整个的东西$IN
,每次输入一行分隔;
:
while IFS=';' read -ra ADDR; do for i in "${ADDR[@]}"; do # process "$i" done done <<< "$IN"
取自Bash shell脚本拆分数组:
IN="bla@some.com;john@home.com" arrIN=(${IN//;/ })
说明:
这种结构替换所有出现';'
(初始//
意味着全球替换)字符串中IN
与' '
(一个空格),然后解释空格分隔字符串数组(这是周围的括号做).
花括号内用于用';'
字符替换每个字符的语法' '
称为参数扩展.
有一些常见的问题:
如果原始字符串有空格,则需要使用IFS:
IFS=':'; arrIN=($IN); unset IFS;
如果原始字符串包含空格且分隔符是新行,则可以使用以下命令设置IFS:
IFS=$'\n'; arrIN=($IN); unset IFS;
如果您不介意立即处理它们,我喜欢这样做:
for i in $(echo $IN | tr ";" "\n") do # process done
您可以使用这种循环来初始化数组,但可能有一种更简单的方法.但希望这会有所帮助.
对于这个问题,在bash中已经有很多不同的方法可以做到这一点.但是bash有许多特殊功能,所谓的bashism运行良好,但是在任何其他shell中都不行.
特别是,数组,关联数组和模式替换是纯粹的基础,并且可能在其他shell下不起作用.
在我的Debian GNU/Linux上,有一个名为dash的标准 shell ,但我知道很多人喜欢使用ksh.
最后,在非常小的情况下,有一个名为busybox的特殊工具,带有自己的shell解释器(ash).
SO问题中的字符串示例是:
IN="bla@some.com;john@home.com"
因为这可能对空格很有用,并且因为空格可以修改例程的结果,所以我更喜欢使用这个示例字符串:
IN="bla@some.com;john@home.com;Full Name"
在纯 bash下,我们可以使用数组和IFS:
var="bla@some.com;john@home.com;Full Name"
oIFS="$IFS" IFS=";" declare -a fields=($var) IFS="$oIFS" unset oIFS
IFS=\; read -a fields <<<"$IN"
在最近的bash下使用此语法不会更改$IFS
当前会话,但仅针对当前命令:
set | grep ^IFS= IFS=$' \t\n'
现在,字符串var
被拆分并存储到一个数组(命名fields
)中:
set | grep ^fields=\\\|^var= fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name") var='bla@some.com;john@home.com;Full Name '
我们可以请求变量内容declare -p
:
declare -p IN fields declare -- IN="bla@some.com;john@home.com;Full Name" declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name ")
read
是最快速的分割方式,因为没有分叉,也没有外部资源.
从那里,您可以使用您已知的语法来处理每个字段:
for x in "${fields[@]}";do echo "> [$x]" done > [bla@some.com] > [john@home.com] > [Full Name]
处理后丢弃每个字段(我喜欢这种移动方法):
while [ "$fields" ] ;do echo "> [$fields]" fields=("${fields[@]:1}") done > [bla@some.com] > [john@home.com] > [Full Name]
或者甚至是简单的打印输出(更短的语法):
printf "> [%s]\n" "${fields[@]}" > [bla@some.com] > [john@home.com] > [Full Name]
你可以玩mapfile
:
mapfile -td \; fields < <(printf "%s\0" "$IN")
此语法保留特殊字符,换行符和空字段!
如果你不关心空字段,你可以:
mapfile -td \; fields <<<"$IN" fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'
但你可以通过函数使用字段:
myPubliMail() { printf "Seq: %6d: Sending mail to '%s'..." $1 "$2" # mail -s "This is not a spam..." "$2"(Nota:
\0
格式字符串末尾没用,而你不关心字符串末尾的空字段)mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail将呈现如下内容:
Seq: 0: Sending mail to 'bla@some.com', done. Seq: 1: Sending mail to 'john@home.com', done. Seq: 2: Sending mail to 'Full Name', done. 或者
<<<
在函数中添加bash语法添加的换行符:myPubliMail() { local seq=$1 dest="${2%$'\n'}" printf "Seq: %6d: Sending mail to '%s'..." $seq "$dest" # mail -s "This is not a spam..." "$dest"将呈现相同的输出:
Seq: 0: Sending mail to 'bla@some.com', done. Seq: 1: Sending mail to 'john@home.com', done. Seq: 2: Sending mail to 'Full Name', done. 基于shell中的分隔符拆分字符串
但是如果你想在许多贝壳下写一些可用的东西,你就不得使用玄武.
在许多shell中使用了一种语法,用于在子字符串的第一次或最后一次出现时拆分字符串:
${var#*SubStr} # will drop begin of string up to first occur of `SubStr` ${var##*SubStr} # will drop begin of string up to last occur of `SubStr` ${var%SubStr*} # will drop part of string from last occur of `SubStr` to the end ${var%%SubStr*} # will drop part of string from first occur of `SubStr` to the end(缺少这是我的答案发布的主要原因;)
正如Score_Under所指出:
#
并%
删除最短的匹配字符串,和
##
并%%
删除尽可能长的.字符串的左(开始)的位置
#
和##
意思,和
%
和%%
meand 从字符串的右边(结束).这个小样本脚本在bash,dash,ksh,busybox下运行良好,并且在Mac-OS的bash下进行了测试:
var="bla@some.com;john@home.com;Full Name" while [ "$var" ] ;do iter=${var%%;*} echo "> [$iter]" [ "$var" = "$iter" ] && \ var='' || \ var="${var#*;}" done > [bla@some.com] > [john@home.com] > [Full Name ] 玩得开心!
`#`,`##`,`%`和`%%`替换使IMO更容易解释(删除多少):`#`和`%`删除最短的匹配字符串和`##`和`%%`删除最长的.
5> DougW..:我已经看到了几个引用该
cut
命令的答案,但它们都被删除了.没有人详细说明这一点有点奇怪,因为我认为它是执行此类事情的更有用的命令之一,尤其是用于解析分隔的日志文件.在将此特定示例拆分为bash脚本数组的情况下,
tr
可能更有效,但cut
可以使用,如果要从中间提取特定字段,则更有效.例:
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1 bla@some.com $ echo "bla@some.com;john@home.com" | cut -d ";" -f 2 john@home.com显然,您可以将其放入循环中,并迭代-f参数以独立地拉出每个字段.
当您使用包含以下行的分隔日志文件时,这会变得更有用:
2015-04-27|12345|some action|an attribute|meta data
cut
能够使用cat
此文件并选择特定字段进行进一步处理非常方便.
感谢使用`cut`,它是适合这项工作的合适工具!比任何贝壳黑客都清除得多.
只有事先知道元素的数量,这种方法才有效; 你需要围绕它编写一些更多的逻辑.它还为每个元素运行外部工具.
6> Steven Lizar..:这对我有用:
string="1;2" echo $string | cut -d';' -f1 # output is 1 echo $string | cut -d';' -f2 # output is 2
cut仅将单个char作为分隔符。
7> 小智..:这种方法怎么样:
IN="bla@some.com;john@home.com" set -- "$IN" IFS=";"; declare -a Array=($*) echo "${Array[@]}" echo "${Array[0]}" echo "${Array[1]}"资源
+1 ......但"set"和声明-a是不必要的.你也可以只用'IFS';" && Array =($ IN)`
+1 ......但我不会将变量命名为"数组"......我想是宠物.好的解决方案
-1:首先,@ is是正确的,因为这里的大多数命令都不起作用.其次,它使用分词来形成数组,并且在执行此操作时不执行任何操作来禁止glob-expansion(因此,如果在任何数组元素中都有glob字符,则这些元素将替换为匹配的文件名).
8> 小智..:这也有效:
IN="bla@some.com;john@home.com" echo ADD1=`echo $IN | cut -d \; -f 1` echo ADD2=`echo $IN | cut -d \; -f 2`小心,这个解决方案并不总是正确的.如果您仅传递"bla@some.com",它会将其分配给ADD1和ADD2.
9> lothar..:echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g' bla@some.com john@home.com
-1**如果字符串包含空格怎么办?**例如`IN ="这是第一行;这是第二行"arrIN =($(echo"$ IN"| sed -e's /;/\n/g'))`在这种情况下将生成一个包含8个元素的数组(每个单词空间分隔一个元素),而不是2(每个行的一个元素,半冒号分隔)
@Luca没有sed脚本正好创建两行.什么为你创建多个条目是当你把它放入一个bash数组(默认情况下在白色空间分割)
10> Tony..:我认为AWK是解决问题的最佳和最有效的命令.几乎每个Linux发行版中都默认将AWK包含在Bash中.
echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'会给
bla@some.com john@home.com当然,您可以通过重新定义awk打印字段来存储每个电子邮件地址.
甚至更简单:回声"bla@some.com; john@home.com"| awk'BEGIN {RS =";"} {print}'
11> nickjb..:对Darron的回答有不同的看法,我就是这样做的:
IN="bla@some.com;john@home.com" read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
诊断:`IFS =";"`赋值仅存在于`$(...; echo $ IN)`子shell中; 这就是为什么一些读者(包括我)最初认为它不起作用的原因.我假设所有$ IN都被ADDR1哄骗了.但是nickjb是正确的; 它确实有效.原因是`echo $ IN`命令使用$ IFS的当前值解析其参数,但随后使用空格分隔符将它们回显到stdout,而不管$ IFS的设置如何.所以净效果好像有人叫`读ADDR1 ADDR2 <<<"bla@some.com john@home.com"`(注意输入是空格分隔的;不是分开的).
12> gniourf_gnio..:在Bash中,一种防弹方式,即使您的变量包含换行符也可以使用:
IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")看:
$ in=$'one;two three;*;there is\na newline\nin this field' $ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in") $ declare -p array declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is a newline in this field")'这项工作的诀窍是使用带有空分隔符
-d
的read
(分隔符)选项,以便read
强制读取它所提供的所有内容.我们read
准确地提供变量的内容in
,没有尾随的换行符,这要归功于printf
.请注意,我们还将分隔符放入printf
以确保传递给的字符串read
具有尾随分隔符.没有它,read
将修剪潜在的尾随空字段:$ in='one;two;three;' # there's an empty field $ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in") $ declare -p array declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'尾随空字段被保留.
Bash的更新≥4.4从Bash 4.4开始,内置
mapfile
(又名readarray
)支持-d
指定分隔符的选项.因此另一种规范方式是:mapfile -d ';' -t array < <(printf '%s;' "$in")
我发现它是该列表中的罕见解决方案,它可以同时与`\n`,空格和`*`一起正常工作.而且,没有循环; 执行后,shell中可以访问数组变量(与最高的upvoted答案相反).注意,`in = $'...'`,它不适用于双引号.我认为,它需要更多的赞成.
13> Darron..:如果您没有使用数组,这个衬垫怎么样:
IFS=';' read ADDR1 ADDR2 <<<$IN
14> kenorb..:这是一个干净的3班轮:
in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof" IFS=';' list=($in) for item in "${list[@]}"; do echo $item; done其中
IFS
基于分隔符分隔单词并()
用于创建数组.然后[@]
用于将每个项目作为单独的单词返回.如果你之后有任何代码,你还需要恢复
$IFS
,例如unset IFS
.
使用`$ in` unquoted可以扩展通配符.
15> Emilien Brig..:没有设置IFS
如果您只有一个冒号,您可以这样做:
a="foo:bar" b=${a%:*} c=${a##*:}你会得到:
b = foo c = bar
16> Halle Knast..:以下Bash/zsh函数在第二个参数给定的分隔符上拆分其第一个参数:
split() { local string="$1" local delimiter="$2" if [ -n "$string" ]; then local part while read -d "$delimiter" part; do echo $part done <<< "$string" echo $part fi }例如,命令
$ split 'a;b;c' ';'产量
a b c例如,该输出可以通过管道传输给其他命令.例:
$ split 'a;b;c' ';' | cat -n 1 a 2 b 3 c与给出的其他解决方案相比,这个解决方案具有以下优点:
IFS
未被覆盖:由于偶数局部变量的动态范围,覆盖IFS
循环会导致新值泄漏到循环内执行的函数调用中.不使用数组:使用Bash和zsh中
read
的标志将字符串读入数组.-a
-A
如果需要,可以将该函数放入脚本中,如下所示:
#!/usr/bin/env bash split() { # ... } split "$@"
17> shuaihanhung..:您可以在许多情况下使用awk
echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'你也可以用这个
echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"
18> Victor Choy..:有一个简单而聪明的方式:
echo "add:sfff" | xargs -d: -i echo {}但你必须使用gnu xargs,BSD xargs cant支持-d delim.如果你像我一样使用苹果mac.你可以安装gnu xargs:
brew install findutils然后
echo "add:sfff" | gxargs -d: -i echo {}
19> MageProspero..:这是最简单的方法.
spo='one;two;three' OIFS=$IFS IFS=';' spo_array=($spo) IFS=$OIFS echo ${spo_array[*]}