I'm assuming that the list of pathnames may possibly not be sorted, and that the resulting list of pathnames should be in the same order as in the input. I also assume that no pathname contains embedded newline characters.
Using /bin/sh
:
#!/bin/sh
set --
while IFS= read -r pathname; do
for p do
case $pathname in ("$p"/*) continue 2 ;; esac
done
set -- "$@" "$pathname"
done <list
printf '%s\n' "$@"
This reads the pathnames from the file list
, one line at a time. The accepted pathnames (initially an empty list) are tested against each read pathname, one at a time in the inner loop. If an accepted pathname is a directory path prefix to the current pathname, the current pathname is discarded (the inner loop skips to the next iteration of the outer loop using continue 2
). If no accepted pathname is found to be a directory path prefix to the current pathname, the current pathname is accepted.
The list of accepted pathnames is kept in the positional parameters.
The bash
shell would obviously be able to run the above script, but if you want something written specifically for that shell, you could say
#!/bin/bash
accepted=()
while IFS= read -r pathname; do
for p in "${accepted[@]}"; do
[[ $pathname == "$p"/* ]] && continue 2
done
accepted+=("$pathname")
done <list
printf '%s\n' "${accepted[@]}"
Using awk
with the identical approach as above:
$ awk '{ for (i=1; i<=n; ++i) if (index($0, accepted[i] "/") == 1) next; accepted[++n]=$0 } END { for (i=1; i<=n; ++i) print accepted[i] }' list
/a/b
/a/e/f/g/h
/a/e/f/g/m/n/o
/a/e/f/g/m/n/p
The awk
code, nicified:
{
for (i = 1; i <= n; ++i)
if (index($0, accepted[i] "/") == 1)
next
accepted[++n] = $0
}
END {
for (i = 1; i <= n; ++i)
print accepted[i]
}
You should be able to see the obvious similarities between this awk
program and the shell code variations at the start.
This uses index()
to test whether an accepted pathname is the prefix to the current pathname. You could have used if ($0 ~ "^" acceped[i] "/")
instead, but a shortcoming of this is that the pathnames themselves are used as part of the regular expression. This starts to matter once you have pathnames containing characters like .
and *
etc.
/a
, so all except/a
should be deleted?/a/b
is in the list and is the parent directory of/a/b/c
and/a/b/c/d
, so/a/b/c
and/a/b/c/d
are to be removed. Already have an accepted answer, though, so feel free to move on to another post. Thanks for trying to help, though, and sorry if my question wasn't sufficiently clear.-prune
arg to not have this problem in the first place? (It can be a bit tricky to put it in the right place to trigger on the conditions you want.)