Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash variable substitution vs dirname and basename

Tags:

bash

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 and basename and when the variable substitutions and why?

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?
like image 599
kobame Avatar asked Mar 14 '14 09:03

kobame


1 Answers

The external commands make some logical corrections. Check the result of the next script:

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 

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.

Alternative presentation

Script:

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

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/]]        
like image 182
jm666 Avatar answered Oct 03 '22 11:10

jm666