【发布时间】:2014-12-30 18:30:20
【问题描述】:
我有文件名
hello_1.0_25.tgz
a_hello_1.25.6_154.tgz
<name>_<name1>.tgz
我需要的输出是
hello_1.0
a_hello_1.25.6
<name>
如何在 bash(或)shell 中获取特殊字符 _ 之前的字符串?
【问题讨论】:
我有文件名
hello_1.0_25.tgz
a_hello_1.25.6_154.tgz
<name>_<name1>.tgz
我需要的输出是
hello_1.0
a_hello_1.25.6
<name>
如何在 bash(或)shell 中获取特殊字符 _ 之前的字符串?
【问题讨论】:
在 bash 中,这很简单:
$ f=hello_1.0_25.tgz
$ echo "${f%_*}"
hello_1.0
${f%_*} 只是从变量f 的末尾删除_ 及其后面的任何内容。
这比使用外部工具的其他方法更简洁,并且在不需要时也可以节省使用额外的过程。
【讨论】:
parameter expansion w/substring extraction。干得好。
类似
sed -r 's/(.*)_.*/\1/'
测试
$ echo "hello_1.0_25.tgz" | sed -r 's/(.*)_.*/\1/'
hello_1.0
$ echo "a_hello_1.25.6_154.tgz" | sed -r 's/(.*)_.*/\1/'
a_hello_1.25.6
$ echo "<name>_<name1>.tgz" | sed -r 's/(.*)_.*/\1/'
<name>
它有什么作用?
s替换命令
(.*) 匹配任何内容,直到最后一个 _ 。保存在\1
_.* 匹配 _,然后是其余的
/\1/ 替换为\1,第一个捕获组
或
sed -r 's/_[^_]+$//'
测试
$ echo "hello_1.0_25.tgz" | sed -r 's/_[^_]+$//'
hello_1.0
$ echo "a_hello_1.25.6_154.tgz" | sed -r 's/_[^_]+$//'
a_hello_1.25.6
$ echo "<name>_<name1>.tgz" | sed -r 's/_[^_]+$//'
<name>
它有什么作用?
[^_]+ 匹配除_ 之外的任何内容。 + 将前一个模式量化一次或多次
$ 匹配行尾
// 替换为空
【讨论】:
这 sed 行应该这样做:
sed 's/_[^_]*$//'
用你的例子做个小测试:
kent$ cat f
hello_1.0_25.tgz
a_hello_1.25.6_154.tgz
<name>_<name1>.tgz
kent$ sed 's/_[^_]*$//' f
hello_1.0
a_hello_1.25.6
<name>
awk 肯定也能做到:
kent$ awk -F_ -v OFS="_" 'NF--' f
hello_1.0
a_hello_1.25.6
<name>
或grep,如果你喜欢:
kent$ grep -Po '.*(?=_[^_]*$)' f
hello_1.0
a_hello_1.25.6
<name>
@Tom Fenech 的 bash 方式也不错。
【讨论】:
substring extraction 略有不同:
$ m="a_hello_1.25.6_154.tgz"
$ echo "${m/%_${m/#*_/}/}"
$ a_hello_1.25.6
基本上是说${m/#*_/} 找到最后一个_之后的文字 = 154.tgz(叫它stuff);然后从字符串的后端删除它,前面加下划线 ${m/%_stuff/}。对于${m/%_${m/#*_/}/}的完整表达。
【讨论】:
试试这个。
sed 's/\(.*\)_\.*/\1/g' file_name
【讨论】:
使用 Bash 正则表达式:
$ f=hello_1.0_25.tgz
$ if [[ $f =~ (.*)_.*\.tgz$ ]]; then echo "${BASH_REMATCH[1]}"; fi
hello_1.0
【讨论】:
这个awk 应该这样做:
awk -F_ '{$NF="";sub(/_$/,"")}1' OFS=_ file
hello_1.0
a_hello_1.25.6
<name>
-F_ 将字段分隔符设置为 _$NF="" 删除最后一个字段。sub(/_$/,"") 删除最后一个字段分隔符。1 打印出所有行。
【讨论】:
awk -F_ 'NF--' OFS=_ file
NF 变为负数。这将正常工作,但会删除空白行 awk -F_ 'NF&&NF--' OFS=_ file
awk -F_ '!NF||NF--' OFS=_ file
awk 在某些情况下可能比sed 中的复杂regex 更容易理解