用定界符分割字符串并获取第N个元素


75

我有一个字符串:

one_two_three_four_five

我需要保存上面的字符串中的变量Atwo和变量Bfour

Answers:


105

使用cut_作为字段分隔符,并获得所需的字段:

A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"

您还可以使用echo和管道代替Here字符串:

A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"

例:

$ s='one_two_three_four_five'

$ A="$(cut -d'_' -f2 <<<"$s")"
$ echo "$A"
two

$ B="$(cut -d'_' -f4 <<<"$s")"
$ echo "$B"
four

有没有其他选择?我正在使用ksh(不是bsh),并且返回ksh:语法错误:“ <”意外
Alex

@Alex检查我的编辑。
heemayl

好的答案,我有一个小问题:如果您的变量“ $ s”是路径文件夹,会发生什么。当我尝试剪切路径文件夹时,我会像这样:`$ FILE = my_user / my_folder / [file] *` $ echo $FILE my_user/my_folder/file.csv $ A="$(cut -d'/' -f2 <<<"$FILE")" $ echo $A [file]* 您知道这里发生了什么吗?
亨利·纳瓦罗

1
如果只希望最后一个字段,则仅使用shell内置函数-无需指定其位置,或者不知道字段数时:echo "${s##*_}"
Amit Naidu

19

仅使用POSIX sh构造,就可以使用参数替换构造来一次解析一个定界符。请注意,此代码假定存在必需的字段数,否则将重复最后一个字段。

string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"

或者,您可以在禁用通配符扩展的情况下使用不带引号的参数替换,并将其IFS设置为定界符(仅当定界符为单个非空白字符或任何空白序列为定界符时,此方法才有效)。

string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS

这使位置参数变得混乱。如果在函数中执行此操作,则仅影响函数的位置参数。

另一种方法是使用read内置函数。

IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF

使用unset IFS不会返回IFS默认值。如果在那之后有人OldIFS="$IFS"在OldIFS中将有一个空值。同样,假设IFS的先前值为默认值,这很可能(并且非常有用)不是默认值。唯一正确的解决方案是存储在old="$IFS"IFS =“ $ old”中,然后再还原。或者...使用子外壳(...)。或者,更好的是,阅读我的答案。
sorontar

@sorontar unset IFS不会还原IFS为默认值,但是它将字段拆分恢复为默认效果。是的,这是一个限制,但实际上通常是可以接受的。子外壳的问题是我们需要从中获取数据。我确实展示了一个解决方案,该解决方案最终不会更改状态read。(它可以在POSIX shell中使用,但是IIRC不能在Bourne shell中使用,因为read由于here-document ,它将在子shell中运行。)<<<在您的答案中使用as是仅在ksh / bash / zsh中起作用的一种变体。
吉尔斯(Gilles)

即使在子外壳上使用att或祖传遗物外壳,我也看不到任何问题。所有测试的外壳(包括旧的bourne)在主外壳中提供正确的值。
sorontar

如果我的道路是什么样的话会怎样user/my_folder/[this_is_my_file]*?当我按照这些步骤操作时,我得到的是[this_is_my_file]*
Henry Navarro

@HenryNavarro此输出与我的答案中的任何代码段都不对应。他们都没有做任何特别的事情/
吉尔斯

17

想看一个awk答案,所以这是一个:

A=$(awk -F_ '{print $2}' <<< 'one_two_three_four_five')
B=$(awk -F_ '{print $4}' <<< 'one_two_three_four_five')

1
如果需要最后一块-无需指定其位置或不知道字段数:awk -F_ '{print $NF}' <<< 'one_two_3_4_five'
阿米特·奈杜

8

最简单的方法(对于带有<<<的shell)是:

 IFS='_' read -r a second a fourth a <<<"$string"

使用时间变量$a而不是$_因为一个shell抱怨。

在完整脚本中:

 string='one_two_three_four_five'
 IFS='_' read -r a second a fourth a <<<"$string"
 echo "$second $fourth"

没有更改IFS,没有set -f(路径名扩展)问题位置参数(“ $ @”)没有更改。


对于可移植到所有 shell(是的,包括所有POSIX)而无需更改IFS或的解决方案set -f,请使用(稍微复杂一点)heredoc等效项:

string='one_two_three_four_five'

IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_

echo "$second $fourth"

请理解,此解决方案(here-doc和的使用都<<<将删除所有尾随的换行符。
并且此
解决方案旨在处理“单一衬里”可变内容。可以使用多衬套解决方案,但需要更复杂的构造。


bash 4.4版中可能有一个非常简单的解决方案

readarray -d _ -t arr <<<"$string"

echo "array ${arr[1]} ${arr[3]}"   # array numbers are zero based.

POSIX Shell没有等效项,因为许多POSIX Shell没有数组。

对于具有数组的shell来说可能很简单:(
经过attsh,lksh,mksh,ksh和bash的测试)

set -f; IFS=_; arr=($string)

但是还有很多其他方法可以保留和重置变量和选项:

string='one_* *_three_four_five'

case $- in
    *f*) noglobset=true; ;;
    *) noglobset=false;;
esac

oldIFS="$IFS"

set -f; IFS=_; arr=($string)

if $noglobset; then set -f; else set +f; fi

echo "two=${arr[1]} four=${arr[3]}"

在zsh中,数组从1开始,默认情况下不拆分字符串。
因此,需要做一些更改才能使它在zsh中工作。


read 只要OP不想从长字符串中提取第76和127个元素,使用的解决方案
就很简单

@don_crissti当然,是的,但是有一个类似的结构:readarray在这种情况下可能更容易使用。
sorontar

@don_crissti我还为确实具有数组的shell添加了数组解决方案。对于没有数组的POSIX外壳,无论如何,最多127个元素的位置参数都不是“简单”的解决方案。
sorontar

2

有了zsh你可以分割字符串(上_)到一个数组:

elements=(${(s:_:)string})

然后通过数组索引访问每个元素:

print -r ${elements[4]}

请记住,zsh(不同于ksh/ bash数组中的索引从1开始


请记住set -f在第一个解决方案中添加警告。... *也许是星号?
sorontar

@sorontar-您为什么认为我需要set -f?我没有使用read/ IFS。尝试使用类似*_*_*或类似字符串的解决方案...
don_crissti

不是针对zsh,而是用户要求一个ksh解决方案,因此,他可以尝试在该shell中使用它。警告将帮助他避免该问题。
sorontar

1

是否允许使用python解决方案?

# python -c "import sys; print sys.argv[1].split('_')[1]" one_two_three_four_five
two

# python -c "import sys; print sys.argv[1].split('_')[3]" one_two_three_four_five
four

不,不好,不好回答
Raj Kumar,

0

另一个awk示例;更容易理解。

A=\`echo one_two_three_four_five | awk -F_ '{print $1}'\`  
B=\`echo one_two_three_four_five | awk -F_ '{print $2}'\`  
C=\`echo one_two_three_four_five | awk -F_ '{print $3}'\`  
... and so on...  

也可以与变量一起使用。
假设:
this_str =“ one_two_three_four_five”
然后,下面的工作:
A =`echo $ {this_str} | awk -F_'{print $ 1}'`
B =`echo $ {this_str} | awk -F_'{print $ 2}'`
C =`echo $ {this_str} | awk -F_'{print $ 3}'`
...依此类推...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.