powershell/python_shell编写可以指定递归深度的powershell tree命令/ls递归遍历符号链接symlink/仅遍历文件夹/遍历文件和目录/指定遍历深度/如何绘制树干

ls(get-childItem)递归遍历符号链接symlink

-FollowSymlink <System.Management.Automation.SwitchParameter>
        By default, the `Get-ChildItem` cmdlet displays symbolic links to directories found during recursion, but doesn't recurse into them. Use the FollowSymlink parameter to search the
         directories that target those symbolic links. The FollowSymlink is a dynamic parameter and is supported only in the FileSystem provider.
##  powershell version
  • 默认情况下,ls -R不会遍历到符号链接/junction link所指的目录
  • 明白这一点在某些情况下很重要(特别是下文中的模拟tree的函数)
    • 该版本也是默认不会遍历进入符号链接,不过,您可以指定-FollowSymlink来迫使遍历能够访问符号链接所指向的目录

powershell version(no tree branch)

  • 可以自行修改或者结合管道符进行更高级的处理
function tree_pwsh
{
    # Closure function
    <# 
    .synopsis
    本函数支持遍历目录和文件
    也可以选择仅遍历目录而部列出文件
    通过缩进来表示嵌套层次关系
    支持指定最大遍历深度;指定为0时,表示不限制深度
    .example
    recurseClosure -traverseType d -maxDepth 3

    recurseClosure -traverseType a -maxDepth 3 -path D:\repos\scripts\linuxShellScripts\
    排除关键字示例(可以修改-eq为-like / -match 来支持通配符或正则表达式)
    recurseTree -exclude "node_modules"  -maxDepth 0 |sls -Pattern "Rand.*"
    #>

    # 参数置顶原则
    param(
        $traverseType = '',
        $path = './',
        $maxDepth = '2',
        $exclude = ''
    )
    
    $depth = 1
    $times = 0
    function listRecurse
    {
        <# 遍历所有子目录 #>
        param(
            $traverseType = '',
            $path = ''
        )
        # Write-Output "`tpath=$path"
        if ($traverseType -eq 'd')
        {
            $lst = (Get-ChildItem -Directory $path)
        }
        else
        {
            $lst = (Get-ChildItem $path)

        }

        # 子目录数目len
        $len = $lst.Length
        $times++;

        #每一层处理都是都是一重循环O(n)
    
        # 遍历子目录
        <# 注意需要添加对文件的判断,否则在对文件调用本函数的时候,会陷入死循环(无法进入深层目录) #>
        $lst | ForEach-Object {
            $len--;
            # Write-Output "`t`t remain times :len=$len";
            if ($_.BaseName -like $exclude)
            {
                # pass it
            }
            else
            {

                # 打印每个子目录及其深度
                # 无树干的前缀字符串(简洁版)
                # $indent = "`t" * ($depth - 1)
                # 如果想要画出所有的枝干,需要在intend这段字符串做改进(不单单是合适数量的制表符.)
                # 总之,每一行可能有多个`|`:第n层的条目,需要有n条树干线(而且,同一行的内容只能够一次性打印完,)
                # 所以,我们应该计算并安排好每一行的前缀字符串(树干线)

                # 带树干的字符串:│ ├ ─  └ ─ |
                $indent_tree = "│`t" * ($depth - 1) + '│'
                # 打印路径
                $pathNameRelative= $_.baseName
                Write-Output "$indent_tree`──($depth)$($pathNameRelative)"

                if ((Get-Item $_) -is [system.io.directoryinfo] )
                {
                    # 打印树干
                    # 其实还要考虑要求打印的深度的截至
                    if (@(Get-ChildItem $_).Count -gt 0  )
                    {
                        # Write-Output @(Get-ChildItem $_).Count
                        # $branch = '|' + "`t" * ($depth - 1) + '  \____.'
                        $branch = $indent_tree + '  ├────'
                        # $branch = $indent_tree 

                        if ($depth -eq $maxDepth)

                        {
                            <# Action to perform if the condition is true #>
                            $branch += '......'
                        }
                        $branch
                    }

                    $depth++
                    # write
                    # 对子目录继续深挖,(做相同的调用)
                    if ($depth -le $maxDepth -or $maxDepth -eq 0)
                    {
                        listRecurse -path $_.FullName -traverseType $traverseType
                    }
                    $depth--
                }
                # Write-Output "$depth"
                # Start-Sleep -Milliseconds 1000
            }
        } 
    }   

    listRecurse -traverseType $traverseType -path $path
    # listRecurse

}

示例效果

PS D:\repos\blogs\neep> recurseTree -maxDepth 3
 1 D:\repos\blogs\neep\408
         2 D:\repos\blogs\neep\408\ComputerNetwork
                 3 D:\repos\blogs\neep\408\ComputerNetwork\concepts.md
                 3 D:\repos\blogs\neep\408\ComputerNetwork\ipv6.md
         2 D:\repos\blogs\neep\408\os
                 3 D:\repos\blogs\neep\408\os\exercise5.md
         2 D:\repos\blogs\neep\408\principlesOfCompilers
                 3 D:\repos\blogs\neep\408\principlesOfCompilers\image
                 3 D:\repos\blogs\neep\408\principlesOfCompilers\subSet.md
         2 D:\repos\blogs\neep\408\principlesOfComputer
                 3 D:\repos\blogs\neep\408\principlesOfComputer\机器数问题
                 3 D:\repos\blogs\neep\408\principlesOfComputer\image
                 3 D:\repos\blogs\neep\408\principlesOfComputer\1646647596000.png
                 3 D:\repos\blogs\neep\408\principlesOfComputer\补码和模的故事.md
                 3 D:\repos\blogs\neep\408\principlesOfComputer\存储单元地址.md

powershell tree_pwsh(with tree branch)

function tree_pwsh
{
    # Closure function
    <# 
    .synopsis
    本函数支持遍历目录和文件
    也可以选择仅遍历目录而部列出文件
    通过缩进来表示嵌套层次关系
    支持指定最大遍历深度;指定为0时,表示不限制深度
    .example
    recurseClosure -traverseType d -maxDepth 3

    recurseClosure -traverseType a -maxDepth 3 -path D:\repos\scripts\linuxShellScripts\
    排除关键字示例(可以修改-eq为-like / -match 来支持通配符或正则表达式)
    recurseTree -exclude "node_modules"  -maxDepth 0 |sls -Pattern "Rand.*"
    #>

    # 参数置顶原则
    param(
        $traverseType = '',
        $path = './',
        $maxDepth = '2',
        $exclude = ''
    )
    
    $depth = 1
    $times = 0
    function listRecurse
    {
        <# 遍历所有子目录 #>
        param(
            $traverseType = '',
            $path = ''
        )
        # Write-Output "`tpath=$path"
        if ($traverseType -eq 'd')
        {
            $lst = (Get-ChildItem -Directory $path)
        }
        else
        {
            $lst = (Get-ChildItem $path)

        }

        # 子目录数目len
        $len = $lst.Length
        $times++;

        #每一层处理都是都是一重循环O(n)
    
        # 遍历子目录
        <# 注意需要添加对文件的判断,否则在对文件调用本函数的时候,会陷入死循环(无法进入深层目录) #>
        $lst | ForEach-Object {
            $len--;
            # Write-Output "`t`t remain times :len=$len";
            if ($_.BaseName -like $exclude)
            {
                # pass it
            }
            else
            {

                # 打印每个子目录及其深度
                # 无树干的前缀字符串(简洁版)
                # $indent = "`t" * ($depth - 1)
                # 如果想要画出所有的枝干,需要在intend这段字符串做改进(不单单是合适数量的制表符.)
                # 总之,每一行可能有多个`|`:第n层的条目,需要有n条树干线(而且,同一行的内容只能够一次性打印完,)
                # 所以,我们应该计算并安排好每一行的前缀字符串(树干线)
                # 带树干的字符串
                $indent_tree = "|`t" * ($depth - 1)
                
                Write-Output "$indent_tree`\_($depth)$($_.FullName)"
                if ((Get-Item $_) -is [system.io.directoryinfo] )
                {
                    # 打印树干
                    # 其实还要考虑要求打印的深度的截至
                    if (@(Get-ChildItem $_).Count -gt 0  )
                    {
                        # Write-Output @(Get-ChildItem $_).Count
                        # $branch = '|' + "`t" * ($depth - 1) + '  \____.'
                        $branch = $indent_tree+ '  \____.'

                        if ($depth -eq $maxDepth)

                        {
                            <# Action to perform if the condition is true #>
                            $branch += '......'
                        }
                        $branch
                    }

                    $depth++
                    # write
                    # 对子目录继续深挖,(做相同的调用)
                    if ($depth -le $maxDepth -or $maxDepth -eq 0)
                    {
                        listRecurse -path $_.FullName -traverseType $traverseType
                    }
                    $depth--
                }
                # Write-Output "$depth"
                # Start-Sleep -Milliseconds 1000
            }
        } 
    }   

    listRecurse -traverseType $traverseType -path $path
    # listRecurse

}

效果

在这里插入图片描述

updating version

  • 将每层的文件和目录分类(优先答应文件,然后在处理文件夹,使得结果更加紧凑)
""" 
设定一个递归函数travers_dir(dirName,depthStop,...);
该函数支持指定递归的深度;
同时要求能够体现目录间的层次(通过制表符缩进来表达 🅱 )
具体规则如下:当指定深度depth_stop<=0时,尽可能的递归当前目录下的子目录(否则递归的深度就是depth_stop,或者不超过depth_stop);
默认尽可能递归.
该函数接收一个目录字符串参数,函数进入改目录打印出所有文件名以及目录名此外,如果被打印的对象时目录时,需要以该目录为参数在调用一次traverse_dir
在以下实现中,您不应当传入第三个参数,如果为了安全起见,您可以为其在做一次浅封装,使得函数只有两个参数,而函数内部则调用traverse_dir()
"""
from sys import argv
import os
import os.path as op


# 定义一个空函数,来控制日志打印与否(免注释)
def empyt(obj):
    ...


d = print
# 控制是否打印调试日志
d = empyt
pathOut = "fileOutByTreePyScript"


""" 本函数主要用到:os.listdir()以及os.path.isdir()以及一些判断技巧和debug过程中的控制技巧,去掉日志语句后,代码量较少 """
def separate_line(num=50 ,separator='>'):
    print(f'😎🎶:{num*separator}')

def traverse_dir(dirName=".", stop_depth=0, depth=0):
    if depth == 0:
        separate_line(separator='~')
        print(
            f'😁executing the traverse_dir with the paramters: \n@dirName={dirName},\n@stop_depth={stop_depth},\n@depth={depth}')
        separate_line(separator='^')

    if stop_depth > 0:
        if stop_depth > depth:
            pass
        else:
            return
    d("\t new invoke of traverse_dir()")
    items = os.listdir(dirName)
    # print(items)
    # for item in items:
    #     newPath = op.join(dirName, item)
    #     print(newPath)
    #     print(op.isfile(newPath))
    items.sort(key=lambda item: op.isdir(op.join(dirName, item)))
    # separate_line()
    # print(items)
    
    d(items)
    if (items):
        for item in items:
            # newPath = dirName+"/"+item
            # newPath的存在性可以保证,但是是否为目录需做进一步判断
            newPath = op.join(dirName, item)
            d(newPath)
            # notice the paramter of isdir()
            isdir = op.isdir(newPath)
            if isdir:
                # print(isdir)
                if item == ".git":
                    continue
                d("dirName:"+item+"\twill be enter by new invoke of traverse_dir")
                # leftEmoji = "-------😇-------"
                # rightEmoji = "-------🙂-------"
                fileLogo = "☪ "
                folder_Logo = "📁"
                dir_logo="->"
                depthStr = folder_Logo+"depth:"+str(depth+1)+dir_logo
                indent = depth*"\t"
                dirStr = newPath
                # 
                dirStr = indent+depthStr+ dirStr
                print(dirStr)
                # out(dirStr)f
                traverse_dir(newPath, stop_depth, depth+1)
            else:
                fileStr = depth*"\t"+0*" "+"⭐:"+item
                print(fileStr)
                # out(fileStr)


def append(content, fileName=pathOut):
    with open(fileName, 'a') as fout:
        # 注意换行
        fout.write(content+"\n")


def generate_defaultParams():
    # dirName = "d:/repos/learnPython/ppt_source_code"
    # dirName = "./../algorithm/"
    dirPrefix = "d:/repos/scripts/"
    # dirPost = "algorithm"
    dirPost = ""
    # dirName = op.join(dirPrefix, dirPost)
    return dirPrefix+dirPost


# 当反复调试的时候可以预处理将之前的文件删除
# 如果有必要,可以采用将原来的文件重名名的方式(以输出时间为名字后缀是一种选择)
if op.exists(pathOut):
    # 或者用rename()
    os.remove(pathOut)


if __name__ == "__main__":

    # test
    os.chdir(os.getcwd())
    # print(os.getcwd(), "😁😁😁")
    # traverse_dir(".",2)
    separate_line(separator="-")
    print("😊info:there is in the tree_pyScript.py;\n try to offer the tree server...💕")

    # if the CLI didn't offer the parameter for the script,then run the statement:
    dirName = "."  # default direcotry(current work directory)
    dirName = generate_defaultParams()
    depth = 2

    # modify the dirName according the parameter(parameter judging&correcting...)
    # case0:more than 1 parameter.
    if len(argv) > 1:
        # scriptName=argv[0]
        # case1: more than 2 parameters
        dirName = argv[1]
        if len(argv) > 2:
            # print(f"get the argv from commandLine:{argv[1]}😊{argv[2]}")
            depth = argv[2]  # the 2nd parameter will be regarded as stopDepth(as default);
            if(argv[2].isdigit()):
                depth = int(depth)
            # judge if user input the name parameter(keyword parameter.)
            #todo complete this implement
            if(argv[1].startswith("depth=")):
                depth = int(argv[1][6:])
                print(argv[1][6:])
            else: 
                ...#keep the argv[1] as raw input.(as dirName)
            if(argv[2].startswith("dirName=")):
                dirName = argv[2][len("dirName="):]
        if(argv[1] in ["-?", "?", "/?", "\\?"]):
            print("function usage:traverse_dir([dirName],[depth]💕")
    else:
        print("you do not offer any parameter,now try execute the default behaviour💕")
    
    traverse_dir(dirName, depth)
    tip='😘work dir tips for user:'
    separate_line()
    print(tip,"\n",os.getcwd(),f'😘')
    

preview

在这里插入图片描述

old version

""" 

""" 
设定一个递归函数travers_dir(dirName,depthStop,...);
该函数支持指定递归的深度;
同时要求能够体现目录间的层次(通过制表符缩进来表达 🅱 )
具体规则如下:当指定深度depth_stop<=0时,尽可能的递归当前目录下的子目录(否则递归的深度就是depth_stop,或者不超过depth_stop);
默认尽可能递归.
该函数接收一个目录字符串参数,函数进入改目录打印出所有文件名以及目录名此外,如果被打印的对象时目录时,需要以该目录为参数在调用一次traverse_dir
在以下实现中,您不应当传入第三个参数,如果为了安全起见,您可以为其在做一次浅封装,使得函数只有两个参数,而函数内部则调用traverse_dir()
"""
from sys import argv
import os
import os.path as op


# 定义一个空函数,来控制日志打印与否(免注释)
def empyt(obj):
    ...


d = print
# 控制是否打印调试日志
d = empyt

""" 本函数主要用到:os.listdir()以及os.path.isdir()以及一些判断技巧和debug过程中的控制技巧,去掉日志语句后,代码量较少 """


def traverse_dir(dirName=".", stop_depth=0, depth=0):
    # depth=0
    if stop_depth > 0:
        if stop_depth > depth:
            pass
        else:
            return
    d("\t new invoke of traverse_dir()")
    items = os.listdir(dirName)
    d(items)
    if (items):
        for item in items:
            # newPath = dirName+"/"+item
            # newPath的存在性可以保证,但是是否为目录需做进一步判断
            newPath = op.join(dirName, item)
            d(newPath)
            # notice the paramter of isdir()
            if op.isdir(newPath):
                if item == ".git":
                    continue
                d("dirName:"+item+"\twill be enter by new invoke of traverse_dir")
                leftEmoji = "-------😇-------"
                rightEmoji = "-------🙂-------"
                depthStr = "detpth:"+str(depth+1)
                indent = depth*"\t"
                dirStr = newPath
                fileLogo = "☪ "
                folderLogo = "📁 "
                dirStr = indent+depthStr+folderLogo + dirStr
                print(dirStr)
                # out(dirStr)
                traverse_dir(newPath, stop_depth, depth+1)
            else:
                fileStr = depth*"\t"+"⭐:"+item
                print(fileStr)
                # out(fileStr)


"""  """

pathOut = "file_dir_out"


def append(content, fileName=pathOut):
    with open(fileName, 'a') as fout:
        # 注意换行
        fout.write(content+"\n")


# dirName = "d:/repos/learnPython/ppt_source_code"
# dirName = "./../algorithm/"
dirPrefix = "d:/repos/PythonLearn/"
dirPost = "algorithm"
dirPost = ""
# dirName = op.join(dirPrefix, dirPost)
dirName = dirPrefix+dirPost

# 当反复调试的时候可以预处理将之前的文件删除
# 如果有必要,可以采用将原来的文件重名名的方式(以输出时间为名字后缀是一种选择)
if op.exists(pathOut):
    # 或者用rename()
    os.remove(pathOut)

# 将中途的输出结果(比如日志)输出到文件中(采用append模式)


out = append
depth = 0

if __name__ == "__main__":
    
    # test
    # os.chdir("D:/repos/")
    # traverse_dir(".",2)
    if len(argv) > 1:
        dirName = argv[1]
        if len(argv) > 2:
            # print(f"get the argv from commandLine:{argv[1]}😊{argv[2]}")
            depth = argv[2]
            if(argv[2].isdigit()):
                depth = int(depth)
            if(argv[1].startswith("depth=")):
                depth = int(argv[1][6:])
                print(argv[1][6:])
            if(argv[2].startswith("dirName=")):
                dirName = argv[2][len("dirName="):]
        if(argv[1] in ["-?", "?", "/?", "\\?"]):
            print("traverse_dir(dirName,depth)")
    # else:
    traverse_dir(dirName, depth)

效果

20211126211421

使用方式

python <脚本名> 目录名 深度(0则为无限深度)
例如:
python tr_py d:\repos\web 2

posted @ 2021-11-26 21:17  xuchaoxin1375  阅读(33)  评论(0)    收藏  举报  来源