我将以下字符串存储在shell
中的变量中a1="aaa,bbb3,12,ccc,"
a2="aaa,2,bbb,ccc,"
我想对上面的字符串变量执行一个公共命令,以从路径中删除对象编号。对象编号是2个逗号之间的数字。
所以结果应该是
a1="aaa,bbb3,ccc,"
a2="aaa,bbb,ccc,"
我怎么能这样做?
我试过了:
echo ${a1//,[0-9],/,} ==> wrong result
echo ${a2//,[0-9],/,} ==> good result
此外,我将以下字符串存储在shell
中的变量中a1="aaa,bbb3,#zu_45,ccc,"
a2="aaa,#mn,bbb,ccc,"
a3="aaa,bbb,ccc,#kn,"
我想对上面的字符串变量执行一个通用命令,从路径中删除以#
开头的对象。位于2个逗号之间的对象。
所以结果应该是
a1="aaa,bbb3,ccc,"
a2="aaa,bbb,ccc,"
a3="aaa,bbb,ccc,"
我怎么能这样做?
答案 0 :(得分:3)
您可以使用此sed:
sed -r 's/,(#.*)?[0-9]+//' file
a1="aaa,bbb3,ccc,"
a2="aaa,bbb,ccc,"
a3="aaa,bbb,ccc,"
答案 1 :(得分:2)
对于您的第一个问题,您可以使用extglob
。
$ a1="aaa,bbb3,12,ccc,"
$ a2="aaa,2,bbb,ccc,"
$ shopt -s extglob
$ echo "${a1//,+([0-9]),/,}"
aaa,bbb3,ccc,
$ echo "${a2//,+([0-9]),/,}"
aaa,bbb,ccc,
$
Greg's wiki和许多其他地方都有很好的记录。
对于第二个问题,您可以使用更高级的模式匹配来处理此问题,或者将逗号分隔的元素视为字段并单独处理它们。
首先,模式匹配解决方案。
$ a1="aaa,bbb3,#zu_45,ccc,"
$ a2="aaa,#mn,bbb,ccc,"
$ a3="aaa,bbb,ccc,#kn,"
$ echo "${a1//,#+([^,]),/,}"
aaa,bbb3,ccc,
$ echo "${a2//,#+([^,]),/,}"
aaa,bbb,ccc,
$ echo "${a3//,#+([^,]),/,}"
aaa,bbb,ccc,
$
(我只想指出任何流浪的读者,虽然像这样的模式可能看起来像正则表达式{/ 3}}。)
其次,处理字段等字段的解决方案当然会涉及循环。这是一个单行的例子:
$ a=(${a1//,/ }); unset newa; for i in "${a[@]}"; do if [[ ! $i =~ ^# ]]; then newa="$newa${newa:+,}$i"; fi; done; echo "$newa"
aaa,bbb3,ccc
您可以对其他变量重复此操作。
请注意,这取决于您的数据中没有任何空格,因为这是用于分隔bash数组中字段的字符。
答案 2 :(得分:1)
使用纯bash,你可以这样做:
$ re='(.*),[0-9]+,(.*)'
$ a1="aaa,bbb3,12,ccc,"
$ a2="aaa,2,bbb,ccc,"
$ [[ $a1 =~ $re ]] && echo ${BASH_REMATCH[1]},${BASH_REMATCH[2]}
aaa,bbb3,ccc,
$ [[ $a2 =~ $re ]] && echo ${BASH_REMATCH[1]},${BASH_REMATCH[2]}
aaa,bbb,ccc,
这些echo
可以转换为作业,为您提供所需的内容。
您可以在使用( )
之前和之后捕获零件,而不是进行搜索和替换,它们将存储在特殊数组变量$BASH_REMATCH
中。
对于第二个,你可以做类似的事情:
$ re='(.*),#[^,]*,(.*)'
$ a1="aaa,bbb3,#zu_45,ccc,"
$ a2="aaa,#mn,bbb,ccc,"
$ a3="aaa,bbb,ccc,#kn,"
$ [[ $a1 =~ $re ]] && echo ${BASH_REMATCH[1]},${BASH_REMATCH[2]}
aaa,bbb3,ccc,
$ [[ $a2 =~ $re ]] && echo ${BASH_REMATCH[1]},${BASH_REMATCH[2]}
aaa,bbb,ccc,
$ [[ $a3 =~ $re ]] && echo ${BASH_REMATCH[1]},${BASH_REMATCH[2]}
aaa,bbb,ccc,
使用[^,]
(不是逗号)字符类可确保不使用字符串的其余部分。
你可以把它变成这样的函数:
$ rm_hash () {
> local re='(.*),#[^,]*,(.*)'
> local subject="$1"
> [[ $subject =~ $re ]] && echo ${BASH_REMATCH[1]},${BASH_REMATCH[2]}
> }
$ a1="aaa,bbb3,#zu_45,ccc,"
$ rm_hash $a1
aaa,bbb3,ccc,
答案 3 :(得分:0)
仅限bash解决方案:
...从路径中删除对象编号...
$ a1="aaa,bbb3,12,ccc,"
$ echo ${a1//,+([0-9]),/,}
aaa,bbb3,ccc,
...从路径中删除以#开头的对象...
$ a1="aaa,bbb3,#zu_45,ccc,"
$ echo ${a1//,#*([^,]),/,}
aaa,bbb3,ccc,
以上两种解决方案可以组合成一个单独的操作:
$ a1="aaa,32,bbb3,#zu_45,ccc,"
$ echo ${a1//,@(#*([^,])|+([0-9])),/,}
aaa,bbb3,ccc,
所有这些解决方案都依赖于shell option extglob
的设置。他们使用bash extended patterns来匹配字符串的一部分:
+([0-9])
- 匹配一个或多个数字; #*([^,])
- 匹配以散列[#
]开头的任何字符串,后跟任意数量的非逗号; @(...|...)
- 完全匹配给定模式的一个,这些模式由竖线符号分隔。答案 4 :(得分:0)
不完全是单线,但是防弹方法:
IFS=, read -d '' -ra ary <<< "$variable"
unset ary[${#ary[@]}-1]
将创建一个数组ary
,其中的字段是变量variable
中的字段(字段以逗号分隔)。然后,如果你想删除所有数字:
for i in "${!ary[@]}"; do
[[ ${ary[i]} = +([[:digit:]]) ]] && unset ary[$i]
done
或删除所有以哈希开头的元素:
for i in "${!ary[@]}"; do
[[ ${ary[i]} = \#* ]] && unset ary[$i]
done
最后,把它放回你的变量中:
printf -v variable '%s,' "${ary[@]}"
执行该操作的功能:
remove_number() {
local ary i
IFS=, read -d '' -ra ary <<< "${!1}"
unset ary[${#ary[@]}-1]
for i in "${!ary[@]}"; do
[[ ${ary[i]} = +([[:digit:]]) ]] && unset ary[$i]
done
printf -v "$1" '%s,' "${ary[@]}"
}
演示(假设此功能在会话中定义):
$ a1="aaa,bbb3,12,ccc,"
$ remove_number a1
$ echo "$a1"
aaa,bbb3,ccc,
如果变量中有换行符,这也有效(注意:它不会删除负数......这个数字并不是很清楚,但修改函数来处理这个问题也很简单,请参阅{下面{1}}:
is_number
哦,如果数字出现在第一个字段中(与其他答案不同),它会起作用:
$ a=$'aaa,\n\nnewline here\n,123456,-1234,abc\n,'
$ echo "$a"
aaa,
newline here
,123456,-1234,abc
,
$ remove_number a
$ echo "$a"
aaa,
newline here
,-1234,abc
,
对于“空”列表也很好:
$ a='1234,abc,'
$ b='1234,'
$ remove_number a
$ remove_number b
$ echo "a='$a'"; echo "b='$b'"
a='abc,'
b=','
更实用的方法:如果满足函数给出的条件,则创建一个删除字段的函数:
$ a=','
$ remove_number a
$ echo "$a"
,
然后是几个条件:
remove_cond() {
# $1 is condition
# $2 is name of variable that contains list
local ary i
IFS=, read -d '' -ra ary <<< "${!2}"
unset ary[${#ary[@]}-1]
for i in "${!ary[@]}"; do
"$1" "${ary[i]}" && unset ary[$i]
done
printf -v "$2" '%s,' "${ary[@]}"
}
在当前会话中设置这些:
is_number() {
[[ $1 = ?(-)+([[:digit:]]) ]]
}
is_bang() {
[[ $1 = \#* ]]
}
is_bang_or_number() {
is_number "$1" || is_bang "$1"
}