ste*_*anB 1885 bash shell scripting split
我把这个字符串存储在一个变量中:
IN="bla@some.com;john@home.com"
Run Code Online (Sandbox Code Playgroud)
现在我想通过;
分隔符拆分字符串,以便我有:
ADDR1="bla@some.com"
ADDR2="john@home.com"
Run Code Online (Sandbox Code Playgroud)
我不一定需要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
Run Code Online (Sandbox Code Playgroud)
输出:
> [bla@some.com]
> [john@home.com]
Run Code Online (Sandbox Code Playgroud)
有一个涉及设置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
Run Code Online (Sandbox Code Playgroud)
顺便说一句,我试过的时候
mails2=($IN)
Run Code Online (Sandbox Code Playgroud)
我只是在循环打印时得到第一个字符串,没有括号围绕$IN
它工作.
Joh*_*itb 1153
您可以设置内部字段分隔符(IFS)变量,然后将其解析为数组.当在命令中发生这种情况时,分配IFS
仅发生在该单个命令的环境(to read
)中.然后它根据IFS
变量值将输入解析为一个数组,然后我们可以迭代它.
IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
# process "$i"
done
Run Code Online (Sandbox Code Playgroud)
它将解析由一行分隔的项目;
,将其推入一个数组.处理整个的东西$IN
,每次输入一行分隔;
:
while IFS=';' read -ra ADDR; do
for i in "${ADDR[@]}"; do
# process "$i"
done
done <<< "$IN"
Run Code Online (Sandbox Code Playgroud)
pal*_*rom 901
IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })
Run Code Online (Sandbox Code Playgroud)
说明:
这种结构替换所有出现';'
(初始//
意味着全球替换)字符串中IN
与' '
(一个空格),然后解释空格分隔字符串数组(这是周围的括号做).
花括号内用于用';'
字符替换每个字符的语法' '
称为参数扩展.
有一些常见的问题:
IFS=':'; arrIN=($IN); unset IFS;
IFS=$'\n'; arrIN=($IN); unset IFS;
Chr*_*utz 230
如果您不介意立即处理它们,我喜欢这样做:
for i in $(echo $IN | tr ";" "\n")
do
# process
done
Run Code Online (Sandbox Code Playgroud)
您可以使用这种循环来初始化数组,但可能有一种更简单的方法.但希望这会有所帮助.
F. *_*uri 177
对于这个问题,在bash中已经有很多不同的方法可以做到这一点.但是bash有许多特殊功能,所谓的bashism运行良好,但是在任何其他shell中都不行.
特别是,数组,关联数组和模式替换是纯粹的基础,并且可能在其他shell下不起作用.
在我的Debian GNU/Linux上,有一个名为dash的标准 shell ,但我知道很多人喜欢使用ksh.
最后,在非常小的情况下,有一个名为busybox的特殊工具,带有自己的shell解释器(ash).
SO问题中的字符串示例是:
IN="bla@some.com;john@home.com"
Run Code Online (Sandbox Code Playgroud)
因为这可能对空格很有用,并且因为空格可以修改例程的结果,所以我更喜欢使用这个示例字符串:
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
Run Code Online (Sandbox Code Playgroud)
在纯 bash下,我们可以使用数组和IFS:
var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
Run Code Online (Sandbox Code Playgroud)
oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS
Run Code Online (Sandbox Code Playgroud)
IFS=\; read -a fields <<<"$IN"
Run Code Online (Sandbox Code Playgroud)
在最近的bash下使用此语法不会更改$IFS
当前会话,但仅针对当前命令:
set | grep ^IFS=
IFS=$' \t\n'
Run Code Online (Sandbox Code Playgroud)
现在,字符串var
被拆分并存储到一个数组(命名fields
)中:
set | grep ^fields=\\\|^var=
fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
var='bla@some.com;john@home.com;Full Name <fulnam@other.org>'
Run Code Online (Sandbox Code Playgroud)
我们可以请求变量内容declare -p
:
declare -p IN fields
declare -- IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
Run Code Online (Sandbox Code Playgroud)
read
是最快速的分割方式,因为没有分叉,也没有外部资源.
从那里,您可以使用您已知的语法来处理每个字段:
for x in "${fields[@]}";do
echo "> [$x]"
done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
Run Code Online (Sandbox Code Playgroud)
处理后丢弃每个字段(我喜欢这种移动方法):
while [ "$fields" ] ;do
echo "> [$fields]"
fields=("${fields[@]:1}")
done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
Run Code Online (Sandbox Code Playgroud)
或者甚至是简单的打印输出(更短的语法):
printf "> [%s]\n" "${fields[@]}"
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
Run Code Online (Sandbox Code Playgroud)
你可以玩mapfile
:
mapfile -td \; fields < <(printf "%s\0" "$IN")
Run Code Online (Sandbox Code Playgroud)
此语法保留特殊字符,换行符和空字段!
如果你不关心空字段,你可以:
mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'
Run Code Online (Sandbox Code Playgroud)
但你可以通过函数使用字段:
myPubliMail() {
printf "Seq: %6d: Sending mail to '%s'..." $1 "$2"
# mail -s "This is not a spam..." "$2" </path/to/body
printf "\e[3D, done.\n"
}
mapfile < <(printf "%s\0" "$IN") -td \; -c 1 -C myPubliMail
Run Code Online (Sandbox Code Playgroud)
(Nota:\0
格式字符串末尾没用,而你不关心字符串末尾的空字段)
mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail
Run Code Online (Sandbox Code Playgroud)
将呈现如下内容:
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 <fulnam@other.org>', done.
Run Code Online (Sandbox Code Playgroud)
或者<<<
在函数中添加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" </path/to/body
printf "\e[3D, done.\n"
}
mapfile <<<"$IN" -td \; -c 1 -C myPubliMail
Run Code Online (Sandbox Code Playgroud)
将呈现相同的输出:
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 <fulnam@other.org>', done.
Run Code Online (Sandbox Code Playgroud)
但是如果你想在许多贝壳下写一些可用的东西,你就不得使用玄武.
在许多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
Run Code Online (Sandbox Code Playgroud)
(缺少这是我的答案发布的主要原因;)
正如Score_Under所指出:
#
并%
删除最短的匹配字符串,和
##
并%%
删除尽可能长的.字符串的左(开始)的位置
#
和##
意思,和
%
和%%
meand 从字符串的右边(结束).
这个小样本脚本在bash,dash,ksh,busybox下运行良好,并且在Mac-OS的bash下进行了测试:
var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$var" ] ;do
iter=${var%%;*}
echo "> [$iter]"
[ "$var" = "$iter" ] && \
var='' || \
var="${var#*;}"
done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]
Run Code Online (Sandbox Code Playgroud)
玩得开心!
Dou*_*ugW 151
我已经看到了几个引用该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
Run Code Online (Sandbox Code Playgroud)
显然,您可以将其放入循环中,并迭代-f参数以独立地拉出每个字段.
当您使用包含以下行的分隔日志文件时,这会变得更有用:
2015-04-27|12345|some action|an attribute|meta data
Run Code Online (Sandbox Code Playgroud)
cut
能够使用cat
此文件并选择特定字段进行进一步处理非常方便.
Ste*_*azo 105
这对我有用:
string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2
Run Code Online (Sandbox Code Playgroud)
小智 85
这种方法怎么样:
IN="bla@some.com;john@home.com"
set -- "$IN"
IFS=";"; declare -a Array=($*)
echo "${Array[@]}"
echo "${Array[0]}"
echo "${Array[1]}"
Run Code Online (Sandbox Code Playgroud)
小智 64
这也有效:
IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`
Run Code Online (Sandbox Code Playgroud)
小心,这个解决方案并不总是正确的.如果您仅传递"bla@some.com",它会将其分配给ADD1和ADD2.
lot*_*har 63
echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com
Run Code Online (Sandbox Code Playgroud)
Ton*_*ony 57
我认为AWK是解决问题的最佳和最有效的命令.几乎每个Linux发行版中都默认将AWK包含在Bash中.
echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'
Run Code Online (Sandbox Code Playgroud)
会给
bla@some.com john@home.com
Run Code Online (Sandbox Code Playgroud)
当然,您可以通过重新定义awk打印字段来存储每个电子邮件地址.
nic*_*kjb 32
对Darron的回答有不同的看法,我就是这样做的:
IN="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
Run Code Online (Sandbox Code Playgroud)
gni*_*urf 29
在Bash中,一种防弹方式,即使您的变量包含换行符也可以使用:
IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
Run Code Online (Sandbox Code Playgroud)
看:
$ 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")'
Run Code Online (Sandbox Code Playgroud)
这项工作的诀窍是使用带有空分隔符-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]="")'
Run Code Online (Sandbox Code Playgroud)
尾随空字段被保留.
从Bash 4.4开始,内置mapfile
(又名readarray
)支持-d
指定分隔符的选项.因此另一种规范方式是:
mapfile -d ';' -t array < <(printf '%s;' "$in")
Run Code Online (Sandbox Code Playgroud)
Dar*_*ron 23
如果您没有使用数组,这个衬垫怎么样:
IFS=';' read ADDR1 ADDR2 <<<$IN
Run Code Online (Sandbox Code Playgroud)
ken*_*orb 19
这是一个干净的3班轮:
in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done
Run Code Online (Sandbox Code Playgroud)
其中IFS
基于分隔符分隔单词并()
用于创建数组.然后[@]
用于将每个项目作为单独的单词返回.
如果你之后有任何代码,你还需要恢复$IFS
,例如unset IFS
.
Emi*_*and 17
没有设置IFS
如果您只有一个冒号,您可以这样做:
a="foo:bar"
b=${a%:*}
c=${a##*:}
Run Code Online (Sandbox Code Playgroud)
你会得到:
b = foo
c = bar
Run Code Online (Sandbox Code Playgroud)
sxd*_*thx 11
如此多的答案和如此多的复杂性。尝试一个更简单的解决方案:
echo "string1, string2" | tr , "\n"
Run Code Online (Sandbox Code Playgroud)
tr
(read, translate) 将输入中的第一个参数替换为第二个参数。
因此tr
,“\n”将输入中的逗号替换为换行符,结果变为:
string1
string2
Run Code Online (Sandbox Code Playgroud)
euk*_*ras 10
这里有一些很酷的答案(尤其是错误器),但是对于类似于其他语言中的 split 的东西——这就是我最初问题的意思——我决定这样做:
IN="bla@some.com;john@home.com"
declare -a a="(${IN//;/ })";
Run Code Online (Sandbox Code Playgroud)
现在${a[0]}
、${a[1]}
等等,正如您所期望的那样。用于${#a[*]}
术语数。当然,或者迭代:
for i in ${a[*]}; do echo $i; done
Run Code Online (Sandbox Code Playgroud)
重要的提示:
这适用于无需担心空间的情况,这解决了我的问题,但可能无法解决您的问题。在这种情况下采用$IFS
解决方案。
Hal*_*ast 10
以下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
}
Run Code Online (Sandbox Code Playgroud)
例如,命令
$ split 'a;b;c' ';'
Run Code Online (Sandbox Code Playgroud)
产量
a
b
c
Run Code Online (Sandbox Code Playgroud)
例如,该输出可以通过管道传输给其他命令.例:
$ split 'a;b;c' ';' | cat -n
1 a
2 b
3 c
Run Code Online (Sandbox Code Playgroud)
与给出的其他解决方案相比,这个解决方案具有以下优点:
IFS
未被覆盖:由于偶数局部变量的动态范围,覆盖IFS
循环会导致新值泄漏到循环内执行的函数调用中.
不使用数组:使用Bash和zsh中read
的标志将字符串读入数组.-a
-A
如果需要,可以将该函数放入脚本中,如下所示:
#!/usr/bin/env bash
split() {
# ...
}
split "$@"
Run Code Online (Sandbox Code Playgroud)
您可以在许多情况下使用awk
echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'
Run Code Online (Sandbox Code Playgroud)
你也可以用这个
echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"
Run Code Online (Sandbox Code Playgroud)
有一个简单而聪明的方式:
echo "add:sfff" | xargs -d: -i echo {}
Run Code Online (Sandbox Code Playgroud)
但你必须使用gnu xargs,BSD xargs cant支持-d delim.如果你像我一样使用苹果mac.你可以安装gnu xargs:
brew install findutils
Run Code Online (Sandbox Code Playgroud)
然后
echo "add:sfff" | gxargs -d: -i echo {}
Run Code Online (Sandbox Code Playgroud)
这是最简单的方法.
spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}
Run Code Online (Sandbox Code Playgroud)
如果没有空间,为什么不是这个?
IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)
echo ${arr[0]}
echo ${arr[1]}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2036745 次 |
最近记录: |