下一个脚本
str=/aaa/bbb/ccc.txt
echo "str: $str"
echo ${str##*/} == $(basename $str)
echo ${str%/*} == $(dirname $str)
Run Code Online (Sandbox Code Playgroud)
生产:
str: /aaa/bbb/ccc.txt
ccc.txt == ccc.txt
/aaa/bbb == /aaa/bbb
Run Code Online (Sandbox Code Playgroud)
问题是:
dirname和basename变量替换时为什么?主要是因为:
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
Run Code Online (Sandbox Code Playgroud)
在我的系统上产生:
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
Run Code Online (Sandbox Code Playgroud)
调用外部程序(分叉)需要花费时间.问题的要点是:
jm6*_*666 21
外部命令进行一些逻辑更正.检查下一个脚本的结果:
doit() {
str=$1
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
Run Code Online (Sandbox Code Playgroud)
结果
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
Run Code Online (Sandbox Code Playgroud)
最有趣的结果之一是$(dirname "aaa").外部命令dirname正确返回.但变量扩展${str%/*}返回不正确的值aaa.
脚本:
doit() {
strings=( "[[$1]]"
"[[$(basename "$1")]]"
"[[${1##*/}]]"
"[[$(dirname "$1")]]"
"[[${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//
Run Code Online (Sandbox Code Playgroud)
输出:
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/]]
Run Code Online (Sandbox Code Playgroud)
dirname.如果其参数不包含斜杠/,则输出,因此dirname使用参数替换进行仿真不会产生相同的结果,具体取决于输入.
basename将后缀作为第二个参数,它也将从文件名中删除该组件.您也可以使用参数替换来模拟这一点,但由于您不能同时执行这两项操作,因此它不像使用时那样简短basename.
使用dirname或者basename需要子shell,因为它们不是shell内置的,所以参数替换会更快,特别是在循环中调用它们时(如图所示).
我已经basename在不同系统(/usr/bin,/bin)上的不同位置看到过,因此如果由于某种原因必须在脚本中使用绝对路径,它可能会因为无法找到可执行文件而中断.
所以,是的,有一些事情要考虑,根据情况和输入,我使用两种方法.
编辑:两者dirname和basename实际上在源树中可用作bash可加载builtin的,examples/loadables并且可以使用启用(一旦编译)
enable -f /path/to/dirname dirname
enable -f /path/to/basename basename
Run Code Online (Sandbox Code Playgroud)
使用变量替换的主要缺陷是它们难以阅读和支持.
那当然是主观的!我个人在整个地方使用变量替换.我用read,IFS而set不是awk.我使用bash正则表达式,而bash扩展globbing而不是sed.但那是因为:
a)我想要表现
b)我是唯一一个会看到这些剧本的人
令人遗憾的是,许多需要维护shell脚本的人对语言知之甚少.您必须做出平衡决定:哪个更重要,性能还是可维护性?在大多数情况下,您会发现可维护性获胜.
你必须承认这basename $0是相当明显的,而${0##*/}相当模糊