Bash 变量替换与 dirname 和 basename
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22401091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash variable substitution vs dirname and basename
提问by kobame
The next script
下一个脚本
str=/aaa/bbb/ccc.txt
echo "str: $str"
echo ${str##*/} == $(basename $str)
echo ${str%/*} == $(dirname $str)
produces:
产生:
str: /aaa/bbb/ccc.txt
ccc.txt == ccc.txt
/aaa/bbb == /aaa/bbb
The question is:
问题是:
- In bash scripts, when is it recommended to use commands
dirname
andbasename
and when the variable substitutions and why?
- 在bash脚本,如果是,则建议使用命令
dirname
和basename
当变量替代,为什么?
Asking mainly because:
问主要是因为:
str="/aaa/bbb/ccc.txt"
count=10000
s_cmdbase() {
let i=0
while(( i++ < $count ))
do
a=$(basename $str)
done
}
s_varbase() {
let i=0
while(( i++ < $count ))
do
a=${str##*/}
done
}
s_cmddir() {
let i=0
while(( i++ < $count ))
do
a=$(dirname $str)
done
}
s_vardir() {
let i=0
while(( i++ < $count ))
do
a=${str%/*}
done
}
time s_cmdbase
echo command basename
echo ===================================
time s_varbase
echo varsub basename
echo ===================================
time s_cmddir
echo command dirname
echo ===================================
time s_vardir
echo varsub dirname
on my system produces:
在我的系统上产生:
real 0m33.455s
user 0m10.194s
sys 0m18.106s
command basename
===================================
real 0m0.246s
user 0m0.237s
sys 0m0.007s
varsub basename
===================================
real 0m30.562s
user 0m10.115s
sys 0m17.764s
command dirname
===================================
real 0m0.237s
user 0m0.226s
sys 0m0.007s
varsub dirname
Calling external programs (forking) costs time. The main point of the question is:
调用外部程序(分叉)需要花费时间。问题的主要观点是:
- Are there some pitfalls using variable substitutions instead of external commands?
- 使用变量替换而不是外部命令是否存在一些陷阱?
回答by jm666
The external commands make some logical corrections. Check the result of the next script:
外部命令进行了一些逻辑修正。检查下一个脚本的结果:
doit() {
str=
echo -e "string $str"
cmd=basename
[[ "${str##*/}" == "$($cmd $str)" ]] && echo "$cmd same: ${str##*/}" || echo -e "$cmd different ${str##*/}\t>${str##*/}<\tvs command:\t>$($cmd $str)<"
cmd=dirname
[[ "${str%/*}" == "$($cmd $str)" ]] && echo "$cmd same: ${str%/*}" || echo -e "$cmd different ${str%/*}\t>${str%/*}<\tvs command:\t>$($cmd $str)<"
echo
}
doit /aaa/bbb/
doit /
doit /aaa
doit aaa
doit aaa/
doit aaa/xxx
with the result
结果
string /aaa/bbb/
basename different ${str##*/} >< vs command: >bbb<
dirname different ${str%/*} >/aaa/bbb< vs command: >/aaa<
string /
basename different ${str##*/} >< vs command: >/<
dirname different ${str%/*} >< vs command: >/<
string /aaa
basename same: aaa
dirname different ${str%/*} >< vs command: >/<
string aaa
basename same: aaa
dirname different ${str%/*} >aaa< vs command: >.<
string aaa/
basename different ${str##*/} >< vs command: >aaa<
dirname different ${str%/*} >aaa< vs command: >.<
string aaa/xxx
basename same: xxx
dirname same: aaa
One of most interesting results is the $(dirname "aaa")
. The external command dirname
correctly returns .
but the variable expansion ${str%/*}
returns the incorrect value aaa
.
最有趣的结果之一是$(dirname "aaa")
. 外部命令dirname
正确返回,.
但变量扩展${str%/*}
返回错误值aaa
。
Alternative presentation
替代演示
Script:
脚本:
doit() {
strings=( "[[]]"
"[[$(basename "")]]"
"[[${1##*/}]]"
"[[$(dirname "")]]"
"[[${1%/*}]]" )
printf "%-15s %-15s %-15s %-15s %-15s\n" "${strings[@]}"
}
printf "%-15s %-15s %-15s %-15s %-15s\n" \
'file' 'basename $file' '${file##*/}' 'dirname $file' '${file%/*}'
doit /aaa/bbb/
doit /
doit /aaa
doit aaa
doit aaa/
doit aaa/xxx
doit aaa//
Output:
输出:
file basename $file ${file##*/} dirname $file ${file%/*}
[[/aaa/bbb/]] [[bbb]] [[]] [[/aaa]] [[/aaa/bbb]]
[[/]] [[/]] [[]] [[/]] [[]]
[[/aaa]] [[aaa]] [[aaa]] [[/]] [[]]
[[aaa]] [[aaa]] [[aaa]] [[.]] [[aaa]]
[[aaa/]] [[aaa]] [[]] [[.]] [[aaa]]
[[aaa/xxx]] [[xxx]] [[xxx]] [[aaa]] [[aaa]]
[[aaa//]] [[aaa]] [[]] [[.]] [[aaa/]]
回答by Adrian Frühwirth
dirname
outputs.
if its parameter doesn't contain a slash/
, so emulatingdirname
with parameter substitution does not yield the same results depending on the input.basename
takes a suffix as second parameter which will also remove this component from the filename. You can emulate this as well using parameter substitution but since you cannot do both at once it is not as brief as when usingbasename
.Using either
dirname
orbasename
require a subshell since they are not shell builtins, so the parameter substitution will be faster, especially when calling them in a loop (as you have shown).I have seen
basename
in different locations on different systems (/usr/bin
,/bin
) so if you have to use absolute paths in your script for some reason it might break since it cannot find the executable.
dirname
.
如果其参数不包含 slash/
,则输出,因此dirname
使用参数替换进行模拟不会产生相同的结果,具体取决于输入。basename
将后缀作为第二个参数,这也将从文件名中删除此组件。您也可以使用参数替换来模拟这一点,但由于您不能同时执行这两项操作,因此它不像使用basename
.使用
dirname
或basename
需要子shell,因为它们不是shell 内置函数,因此参数替换会更快,尤其是在循环中调用它们时(如您所示)。我
basename
在不同系统 (/usr/bin
,/bin
) 的不同位置看到过,因此如果出于某种原因必须在脚本中使用绝对路径,它可能会因为找不到可执行文件而中断。
So, yes, there are some things to consider and depending on situation and input I use both methods.
所以,是的,有一些事情需要考虑,根据情况和输入,我使用这两种方法。
EDIT: Both dirname
and basename
are actually available as bash
loadable builtin
s under examples/loadables
in the source tree and can be enabled (once compiled) using
编辑:两者dirname
和basename
实际上都可用作源树下的bash
可加载builtin
s,examples/loadables
并且可以使用(一旦编译)启用
enable -f /path/to/dirname dirname
enable -f /path/to/basename basename
回答by cdarke
The main pitfall in using variable substitutions is that they can be difficult to read and support.
使用变量替换的主要缺陷是它们难以阅读和支持。
That is, of course, subjective! Personally I use variable substitutions all over the place. I use read
, IFS
, and set
instead of awk
. I use bash regular expressions, and bash extended globbing instead of sed
. But that is because:
那当然是主观的!我个人到处都使用变量替换。我使用read
,IFS
和set
而不是awk
。我使用 bash 正则表达式,而 bash 扩展了 globbing 而不是sed
. 但那是因为:
a) I want performance
a) 我想要表现
b) I am the only person who will ever see these scripts
b) 我是唯一会看到这些脚本的人
It is sad to say that many people who have to maintain shell scripts know frightenly little about the language. You have to make a balance decision: which is more important, performance or maintainability? On most occasions you will find that maintainability wins.
遗憾的是,许多必须维护 shell 脚本的人对这种语言知之甚少。您必须做出一个平衡的决定:哪个更重要,性能还是可维护性?在大多数情况下,您会发现可维护性胜出。
You have to admit that basename $0
is fairly obvious, whereas ${0##*/}
is fairly obscure
你不得不承认这basename $0
是相当明显的,而这${0##*/}
是相当晦涩的