Ls a file and everything above it

Sometimes when troubleshooting a problem or just being curious, it’s helpful to see details about a file and all the directories above it — basically the output from ls -lZ filename. The script below – which I call lsup – does that, following symbolic links as well. This can avoid a lot of miscellaneous commands when trying to verify ownership, permissions, and selinux contexts along a file’s path. For example,

utoddl@tango:~$ lsup /usr/bin/sendmail
drwxr-xr-x. 1 root root system_u:object_r:etc_t:s0            7378 Mar 27 06:26 /etc
drwxr-xr-x. 1 root root system_u:object_r:etc_t:s0            1972 Mar 17 06:38 /etc/alternatives
lrwxrwxrwx. 1 root root unconfined_u:object_r:etc_t:s0          25 Apr 19  2025 /etc/alternatives/mta -> /usr/bin/sendmail.postfix
drwxr-xr-x. 1 root root system_u:object_r:usr_t:s0             168 Oct 30 08:31 /usr
dr-xr-xr-x. 1 root root system_u:object_r:bin_t:s0           96022 Mar 27 06:25 /usr/bin
lrwxrwxrwx. 1 root root unconfined_u:object_r:bin_t:s0          21 Apr 17  2025 /usr/bin/sendmail -> /etc/alternatives/mta
-rwxr-xr-x. 1 root root system_u:object_r:sendmail_exec_t:s0 32360 Aug  5  2025 /usr/bin/sendmail.postfix

I hope you find it useful.

#!/usr/bin/bash
#
# lsup - Perform `ls -ldZ` on files and their ascendents
# Copyright (C) 2026  Todd Lewis <utoddl@gmail.com>
# SPDX-License-Identifier:  GPL-2.0+

_lsup ()
{ 
    if [ $# -eq 0 ]; then
        set -- "$PWD"
    fi
    local pd ppd tgt
    local -A pds=()
    while [ $# -gt 0 ]; do
        pd="$1"
        shift
        while [ "$pd" ]; do
            pd=$(realpath -s -m "$pd")
            if [ -L "$pd" ]; then
                tgt="$(readlink "$pd")"
                [ "${tgt:0:1}" = / ] || tgt="${pd%/*}/${tgt}"
                set -- "$@" "$tgt"
            fi
            pds["$pd"]="$pd"
            ppd="$pd"
            pd="${pd%/*}"
            if [ "$ppd" = "$pd" ]; then
                break
            fi
        done
    done
    ls --color=auto -ldZ "${!pds[@]}"
}


_lsup "$@"

By the way, there’s a reason it defines the function _lsup and then immediately calls it once rather than just executing the function’s commands directly. When I’ve remoted into a host without lsup installed, I can copy the function definition from a local terminal (skipping the leading _), then paste it into my remote shell and have all the functionality of lsup in that shell without installing it directly on the remote host. There are other ways to do it, but this works for me.

2 Likes

See also:

namei -l -Z $(type -P sendmail)
1 Like