1

I'm looking to manipulate the output of $tree --noreport$ in such a way that replaces the leading box-drawing characters and spaces on each line with a matching number of spaces. If I were to write the pattern for matching these characters, it would be ^\\(\u2500\\|\u2514\\|\u251C\\| \\)*\u2500. This string would be wrapped in $'...' because Unicode escape sequences are not recognized by sed. This pattern occurs on every line of the output of tree --noreport except for the first. Each character in each matching string needs to be replaced with a space.

Example input:

.
├── docs
│   ├── jokes
│   │   └── knock_knock.txt
│   └── work
├── images
└── .profile

Example output:

.
    docs
        jokes
            knock_knock.txt
        work
    images
    .profile

I'm now realizing for my purposes that I need to remove ambiguity over where a file or folder's name starts (a file or folder's name may begin with one or more spaces), so the output should actually look maybe like:

.
    /docs
        /jokes/
            /knock_knock.txt
        /work
    /images
    /.profile

The \u2500 at the end of my provided pattern actually distinguishes between tree's formatting and the start of the file/folder's name.

1

You could replace one at a time in a loop like:

tree --noreport | sed $'
  :1
    s/^\\( *\\)\\([\ua0\u2500\u2502\u2514\u251C]\\)\\([\ua0\u2500\u2502\u2514\u251C ]*\u2500 \\)/\\1 \\3/
  t1
  s|\u2500 |  /|'

(you missed \ua0 (non-breaking space) and \u2502 ( also shown in your sample) at least, which do show up in the output of my version of tree at least).

That's fooled if there are files whose name contains "─ " optionally preceded by some of those characters.

Another approach (assuming that you do want to insert the / and that the directories you're calling tree on don't contain "─ "): insert a / after the first occurrence of "\u2500 " and then replace every character before the first / with a space in a loop.

tree --noreport | sed '
  s|'$'\u2500'' |&/|;t1
  b
  :1
    s|^\( *\)[^ /]|\1 |
  t1'

It's a bit easier with perl:

tree --noreport | perl -C -pe 's{^.*?\x{2500} }{" " x length($&) . "/"}e'
1

This is a bash function that output the names found in a top directory recursively:

mytree () (
    topdir="${1-.}"
    indent="${2-0}"

    shopt -s nullglob
    shopt -s dotglob

    # output directory name
    printf "%${indent}s'%s'/\n" "" "${topdir##*/}"

    indent=$(( indent + 4 ))

    for name in "$topdir"/*; do
        if [ -h "$name" ]; then
            # output symbolic link name and target
            printf "%${indent}s'%s' --> '%s'\n" "" "${name##*/}" "$( readlink -- "$name" )"
        elif [ -d "$name" ]; then
            # recurse into directories
            mytree "$name" "$indent"
        else
            # output other type of name (not directory or symbolic link)
            printf "%${indent}s'%s'\n" "" "${name##*/}"
        fi
    done
)

The function mytree takes one argument (the second argument is only used in recursive calls) which is the top directory to process. If no arguments are given, it will use the current directory as the top directory to list.

The output is like the following:

'Work'/
    'Data'/
        's3wes.all.tab.frq.counts'
        's3wes.con.tab.frq.counts'
        'sullivan.txt'
    'Development'/
        'ENA-submission'/
            '.git'/
                'COMMIT_EDITMSG'

etc.

That is, names are quoted with single quotes and directories will have a trailing /. In addition, symbolic links will be shown as, for example,

'embl-validator.jar' --> 'embl-api-validator-1.1.158.jar'

The output format can easily be changed by changing the three printf calls.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.