Bash变量替换vs dirname和basename

时间:2014-03-14 09:35:56

标签: bash

下一个脚本

str=/aaa/bbb/ccc.txt
echo "str: $str"
echo ${str##*/} == $(basename $str)
echo ${str%/*} == $(dirname $str)

产生

str: /aaa/bbb/ccc.txt
ccc.txt == ccc.txt
/aaa/bbb == /aaa/bbb

问题是:

  • 在bash脚本中,何时建议使用命令dirnamebasename以及变量替换时为什么?

主要是因为:

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
我系统上的

产生:

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

调用外部程序(分叉)需要花费时间。问题的主要观点是:

  • 使用变量替换而不是外部命令是否存在一些陷阱?

3 个答案:

答案 0 :(得分:20)

外部命令进行一些逻辑更正。检查下一个脚本的结果:

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

结果

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

最有趣的结果之一是$(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//

输出:

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/]]       

答案 1 :(得分:8)

  1. dirname输出.如果其参数不包含斜杠/,那么使用参数替换模拟dirname不会产生相同的结果取决于输入。

  2. basename将后缀作为第二个参数,它也会从文件名中删除此组件。您也可以使用参数替换来模拟这一点,但由于您不能同时执行这两项操作,因此它不像使用basename时那样简短。

  3. 使用dirnamebasename需要子shell,因为它们不是shell内置的,因此参数替换会更快,尤其是在循环中调用它们时(如您所示)

  4. 我在不同系统(basename/usr/bin)的不同位置看到了/bin,所以如果由于某种原因必须在脚本中使用绝对路径,它可能会破坏因为它找不到可执行文件。

  5. 所以,是的,有一些事情要考虑,根据情况和输入,我使用两种方法。

    编辑:dirnamebasename实际上在源树中的bash下可用作builtin可加载examples/loadables,并且可以启用(一旦编译) )使用

    enable -f /path/to/dirname dirname
    enable -f /path/to/basename basename
    

答案 2 :(得分:5)

使用变量替换的主要缺陷是它们难以阅读和支持。

那当然是主观的!我个人在整个地方使用变量替换。我使用readIFSset代替awk。我使用bash正则表达式,并使用扩展的globbing而不是sed。但那是因为:

a)我想要表现

b)我是唯一一个会看到这些剧本的人

这是悲伤地说,谁必须保持shell脚本很多人都知道frightenly知之甚少的语言。您必须做出平衡决定:哪个更重要,性能还是可维护性?在大多数情况下,您会发现可维护性获胜。

你必须承认basename $0相当明显,而${0##*/}相当模糊