我有一个字符串:
one_two_three_four_five

我需要从上述字符串中保存一个变量Atwo和一个变量Bfour
我正在使用ksh。

#1 楼

cut_用作字段定界符并获取所需的字段:

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


还可以使用echo和管道代替此处的字符串:

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


请注意,如果$s包含换行符,则将返回包含第2/4个字段的多行字符串$s的每一行,而不是$s的第2/4个字段。

评论


还有其他选择吗?我正在使用ksh(不是bsh),它返回ksh:语法错误:`<'意外

– Alex
16-09-25在23:14

@Alex检查我的编辑。

–heemayl
16-09-25在23:24

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

–丹尼·赫恩(Danny Hern)
18/12/5在11:55



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

–阿米特·奈杜(Amit Naidu)
19年5月28日在21:45

请注意,由于涉及到至少分支一个进程,执行单独的命令,通过管道和/或临时文件在进程之间多次传递数据,因此与使用Shell的内置拆分运算符的解决方案相比,它的效率要低得多。

–StéphaneChazelas
20年10月10日在6:53

#2 楼

想看到一个awk的答案,所以一个:

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


评论


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

–阿米特·奈杜(Amit Naidu)
19年5月28日在21:38



这正是我想要的!非常感谢,这是最简单直接的方法。

–user1735921
20-11-09在10:58



#3 楼

仅使用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=; fourth=
set +f; unset IFS


此掩盖位置参数。如果在函数中执行此操作,则仅会影响函数的位置参数。

对于不包含换行符的字符串,另一种方法是使用内置的read

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


评论


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

–艾萨克
16-09-26在16:09

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

–吉尔斯'所以-不再是邪恶的'
16-09-26在16:23

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

–艾萨克
16-09-26在19:16

如果我的路径类似于user / my_folder / [this_is_my_file] *,会发生什么?执行这些步骤后,我得到的是[this_is_my_file] *

–丹尼·赫恩(Danny Hern)
18/12/5在12:20

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

–吉尔斯'所以-不再是邪恶的'
18/12/5在17:11

#4 楼

在这里,字符串
(对于带有<<<的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(路径名扩展)没有问题。位置参数(“ $ @”)没有变化。

这里的文档
用于解决方案可移植到所有外壳(是的,包括所有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+
在bash 4.4版中可能有一个非常简单的解决方案
readarray -d _ -t arr <<<"$string"

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

没有POSIX Shell的等效项,因为许多POSIX Shell没有数组。
Arrays
用于贝壳tha t具有数组可能很简单:
(经测试可以在attsh,lksh,mksh,ksh和bash(不是zsh)中工作)
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

IFS=$oldIFS

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

在zsh中,数组从1开始,并且默认情况下不会拆分字符串。
因此,需要进行一些更改才能在zsh中正常工作:
set -F; IFS=_; arr=( $(echo $string) )
echo "two=${arr[2]} four=${arr[4]}"


#5 楼

使用zsh可以将字符串(在_上)分割成一个数组:

non_empty_elements=(${(s:_:)string})
all_elements=("${(@s:_:)string}")


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

print -r -- ${all_elements[4]}


请记住,在zsh中(与大多数其他shell一样,但不同于ksh / bash),数组索引从1开始。

或直接在一个扩展中:

print -r -- "${${(@s:_:)string}[4]}"


评论


@sorontar-为什么您认为我需要设置-f?我没有使用读/ IFS。使用* _ * _ *之类的字符串尝试我的解决方案...

–don_crissti
16 Sep 26 '19:16



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

–艾萨克
16-09-26在19:19



@Isaac,如果要在zsh中禁用通配符,则应将其设置为-o noglob。 zsh的默认仿真模式下的-f选项与csh中的相同,而不是sh中的选项,它用于在不读取conf文件的情况下启动zsh。设置-f以禁用globbing仅适用于sh / ksh仿真(那是您确实需要禁用globbing的仿真,但是您只能使用这些模式来解释sh / ksh代码,因此实际上并不能在这里应用,并且您仍然需要进一步更改代码,以解决sh / ksh的其他功能不足)

–StéphaneChazelas
20年6月10日在6:24

就是@StéphaneChazelas。如果用户想在ksh中使用此解决方案(请阅读我正在使用ksh),那么他可能需要设置-f。不是针对zsh,不是说要在zsh中使用set -f。

–艾萨克
20 Jun 12'在18:09



#6 楼

是否允许使用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)
19年5月9日在7:51

#7 楼

谨对每个发表出色答案的人致以崇高的敬意,我想知道我们是否过度设计了这个问题。仅需简单回答以下问题的三行代码即可:

str="one_two_three_four_five" <-创建字符串

A=$(echo $str | awk -F_ '{print }') <-告诉awk使用_作为定界符并分配第二个字段分配给A

B=$(echo $str | awk -F_ '{print }') <-告诉awk使用_作为定界符,然后将第四个字段分配给B

然后您可以照常使用变量。这是一个示例:

$ echo "The value of A is: $A; the value of B is: $B"
The value of A is: two; the value of B is: four
$ 


评论


该答案已经给出(几次)。尝试使用str ='*'或str ='-n _-o _-p'或str = $'a_b \ n_c_d_e'。

–StéphaneChazelas
20-6-10下午6:35



#8 楼

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

A=$(echo one_two_three_four_five | awk -F_ '{print }')
B=$(echo one_two_three_four_five | awk -F_ '{print }')  
C=$(echo one_two_three_four_five | awk -F_ '{print }')  
... and so on...  


也可以与变量一起使用。

假设:

this_str="one_two_three_four_five"  


然后进行以下工作:

A=$(printf '%s\n' "${this_str}" | awk -F_ '{print }')
B=$(printf '%s\n' "${this_str}" | awk -F_ '{print }')
C=$(printf '%s\n' "${this_str}" | awk -F_ '{print }')
... and so on...  


假设${this_str}不包含换行符,否则它将在变量内容的每一行中返回第一个_,而不是变量内容中的第一个字段。