one_two_three_four_five
我需要从上述字符串中保存一个变量
A
值two
和一个变量B
值four
我正在使用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个字段。#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}
不包含换行符,否则它将在变量内容的每一行中返回第一个_
,而不是变量内容中的第一个字段。
评论
还有其他选择吗?我正在使用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