Bash: get all paths from path

2019-08-04 12:10发布

Say I have the path gui/site/junior/profile.py

How do I get this?:

gui
gui/site
gui/site/junior

Bonus if you tell me how to loop through each path :D

4条回答
beautiful°
2楼-- · 2019-08-04 12:37

You can loop with awk:

awk 'BEGIN{FS=OFS="/"}
     {  for (i=1; i<=NF; i++) {
           for (j=1; j<i; j++)
              printf "%s/", $j
           printf "%s\n", $i
        }
     }' <<< "gui/site/junior/profile.py"

See as one liner:

$ awk 'BEGIN{FS=OFS="/"}{for (i=1; i<=NF; i++) { for (j=1; j<i; j++) printf "%s%s", $j, OFS; printf "%s\n", $i}}' <<< "gui/site/junior/profile.py"
gui
gui/site
gui/site/junior
gui/site/junior/profile.py

This takes advantage of NF, which counts how many fields the current record has. Based on that, it loops from the first up to the last one, printing every time first up to that value.

查看更多
Anthone
3楼-- · 2019-08-04 12:48

a dash answer:

path="gui/site with spaces/junior/profile.py"
oldIFS=$IFS
IFS=/
set -- $(dirname "$path")
IFS=$oldIFS
accumulated=""
for dir in "$@"; do 
  accumulated="${accumulated}${dir}/"
  echo "$accumulated"
done
gui/
gui/site with spaces/
gui/site with spaces/junior/
查看更多
再贱就再见
4楼-- · 2019-08-04 12:58

You can use the shell's built-in splitting facilities. IFS specifies what to split on.

oldIFS=$IFS
IFS=/
set -f
set -- $path
set +f
IFS=$oldIFS
for component in "$@"; do
    echo "$component"
done

This could be refactored in many ways, but I want the change to IFS to only govern the actual splitting.

The use of set to split a string into positional parameters is slightly obscure, but well worth knowing.

You should properly take care to unset IFS if it was originally unset, but I'm skimping on that.

查看更多
闹够了就滚
5楼-- · 2019-08-04 12:59

Perl variant:

perl -F/ -nlE 'say join("/",@F[0..$_])||"/"for(0..$#F-1)' <<< "gui/site with spaces/junior/profile.py"

produces

gui
gui/site with spaces
gui/site with spaces/junior

if you have NULL separates pathnames, you can add 0 to arguments:

perl -F/ -0nlE 'say join("/",@F[0..$_])||"/"for(0..$#F-1)'
          ^

e.g from

printf "/some/path/name/here/file.py\0" |  perl -F/ -nlE 'say join("/",@F[0..$_])||"/"for(0..$#F-1)'
#                                   ^^

produces

/
/some
/some/path
/some/path/name
/some/path/name/here

For iterating over the paths, you can use the next:

origpath="some/long/path/here/py.py"

do_something() {
        local path="$1"
        #add here what you need to do with the partial path
        echo "Do something here with the ==$path=="
}

while read -r part
do
        do_something "$part"
done < <(perl -F/ -nlE 'say join("/",@F[0..$_])||"/"for(0..$#F-1)' <<< "$origpath")

it produces:

Do something here with the ==some==
Do something here with the ==some/long==
Do something here with the ==some/long/path==
Do something here with the ==some/long/path/here==
查看更多
登录 后发表回答