Кажется, что github.com ведет себя невежливо, заявляя, что ресурс не существует, когда делается неаутентифицированный запрос GET -. Обычно wget
сначала пытается запросить ресурс без аутентификации и повторяет попытку с этими данными аутентификации, когда выдается ошибка 401 Not Authenticated.
Это можно обойти, передав --auth-no-challenge
, чтобы wget
отправлял данные аутентификации в первый раз.
Краткое awk решение:
<infile sort -u |awk 'NR==1 || index($0, pre"/")!=1{print; pre=$0}'
Я предполагаю, что список путей не может быть отсортирован, и результирующий список путей должен быть в том же порядке, что и во входных данных. Я также предполагаю, что путь не содержит встроенных символов новой строки.
Использование/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' "$@"
Это считывает пути из файла list
, по одной строке за раз. Принятые пути (изначально представляют собой пустой список )и проверяются на соответствие каждому прочитанному пути, по одному во внутреннем цикле. Если принятое имя пути является префиксом пути к каталогу для текущего имени пути, текущее имя пути отбрасывается (, внутренний цикл переходит к следующей итерации внешнего цикла с использованиемcontinue 2
). Если ни одно принятое имя пути не является префиксом пути к текущему имени пути, принимается текущее имя пути.
Список допустимых путей хранится в позиционных параметрах.
Оболочка bash
, очевидно, сможет запустить приведенный выше скрипт, но если вы хотите что-то написанное специально для этой оболочки, вы можете сказать
#!/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[@]}"
Использование awk
с тем же подходом, что и выше:
$ 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
Код 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]
}
Вы должны увидеть очевидное сходство между этой awk
программой и вариациями шелл-кода в самом начале.
Используется index()
для проверки того, является ли принятый путь префиксом к текущему пути. Вместо этого вы могли бы использовать if ($0 ~ "^" acceped[i] "/")
, но недостатком этого является то, что сами пути используются как часть регулярного выражения.Это начинает иметь значение, когда у вас есть пути, содержащие такие символы, как .
и *
и т. д.
С perl (и подстановкой процесса сортировки, чтобы гарантировать сортировку ввода):
$ perl -lne '
unless (defined($paths) && m:^($paths)/:) {
$paths{$_}++;
$paths=join("|", map +( "\Q$_\E" ), keys %paths);
};
END { print join("\n", sort keys %paths) }' <(sort input.txt)
/a/b
/a/e/f/g/h
/a/e/f/g/m/n/o
/a/e/f/g/m/n/p
Он постепенно строит регулярное выражение, соответствующее путям, которые он уже видел. Если путь ранее не встречался, он добавляет его к хэшу %paths
, который используется как для построения регулярного выражения, так и для содержания списка путей, которые необходимо вывести в конце скрипта.
Каждый путь окружен \Q и \E при добавлении к регулярному выражению, чтобы убедиться, что любые perlre
метасимволы -(, такие как .
, ?
, *
и т. д. )отключены.
Если я не ошибаюсь, в списке нормализованных (*)или, по крайней мере, последовательно представленных путей, отсортированных с помощью обычной лексикографической сортировки, подкаталоги каталога появляются сразу после этого каталога (рекурсивно ). Следовательно, должно быть достаточно посмотреть только на предыдущую (не -пропущенную )строку.
(*Под нормализованным я подразумеваю /foo/bar
или /foo/bar/
, а не, например. /foo/asdf/../bar
или /foo///bar//
. Вывод из find
не будет проблемой, поскольку, хотя он и дает ненормализованный -вывод, если задан ненормализованный -начальный каталог, вывод, по крайней мере, непротиворечив.)
Путь может по-прежнему быть префиксом другого, будучи только родственным, но не родительским, например. /foo
и /foobar
. Чтобы иметь дело с такими случаями, мы можем добавить завершающую косую черту к каждой строке, в которой ее еще нет.
Следовательно, (с добавлением /foo
и /foobar
к тесту и отсутствие попытки игры в гольф с кодом):
$ sort paths.txt | awk '! /\/$/ { $0 = $0 "/" }
last && last == substr($0, 1, length(last)) { next; }
{ last = $0; sub(/\/$/, "", $0); print }'
/a/b
/a/e/f/g/h
/a/e/f/g/m/n/o
/a/e/f/g/m/n/p
/foo
/foobar
Первая строка добавляет косую черту к текущей строке $0
, если необходимо; второй сравнивает строку с последней сохраненной строкой (в last
), если она есть, и отбрасывает совпадающие строки; третий хранит и печатает все неотброшенные строки с удаленной косой чертой. (Удалите sub(...)
, чтобы сохранить их.)
GNU sed с расширенным режимом регулярных выражений -E
. Предыдущая строка без подмножеств сохраняется в резервном пространстве.
< file sort \
| sed -En '
G
/^([^\n]+)\/.*\n\1$/d
s/\n.*//p;h
'
< file sort \
| perl -lne '
$prev //= $_;
print($prev = $_)
if index($_, "$prev/");
'
POSIX sed не позволяет [^\n]
, поэтому мы переписываем конструкции, совместимые с POSIX
< file sort \
| sed -e '
H;x
\|^\(..*\)\n\1/|{
s/\n.*//;h;d
}
g
'
Для списка файлов изfind
вы можете использоватьfind... -prune
чтобы избежать проблемы в первую очередь :исключить нисхождение к дочерним элементам текущего пути, если это сам каталог. В этом Q&A показан пример, и см. справочную страницу. Если вы пытаетесь совместить его с -o
по ИЛИ некоторых других условий, вам может понадобиться find... \( -name 'foo*' -o -name 'bar*' \) -prune
.
Есть и другие случаи, когда он не будет делать то, что вам нужно, поэтому вам понадобится один из других ответов, которые обрабатывают вывод find
.
напр. если вы используете find -name marker.txt -printf '%h\n'
для поиска каталогов, которые содержат marker.txt
, вы не можете использовать -prune
для этого, потому что -name
соответствует только файлу в каталоге, а не самому каталогу.
Или для других будущих читателей, чей список файлов не является прямо из find
.