
전주곡:
경로/파일 목록의 정렬된 입력이 주어지면 공통 경로를 찾는 방법은 무엇입니까?
기술 용어로 번역하면, stdin에서 정렬된 입력을 공급하는 경우 stdin에서 가장 짧은 적절한 접두사를 선택하는 방법은 무엇입니까?
여기서 "접두사"는 일반적인 의미를 갖습니다. 예를 들어 문자열 'abcde'에는 'abc'라는 접두사가 있습니다. 내 샘플 입력은 다음과 같습니다.
$ echo -e '/home/dave\n/home/dave/file1\n/home/dave/sub2/file2'
/home/dave
/home/dave/file1
/home/dave/sub2/file2
이는 다음의 예입니다.연속적인 적절한 접두사 제거stdin에서 다음 명령을 사용합니다 sed
.
$ echo -e '/home/dave\n/home/dave/file1\n/home/dave/sub2/file2' | sed "N; /^\(.*\)\n\1\//D; P; D"
/home/dave/file1
/home/dave/sub2/file2
질문:
내 질문은 방법입니다.적절한 접두사를 유지하십시오대신 해당 접두사가 있는 모든 줄을 제거하세요. 둘 다 사인 /home/dave/file1
이고 /home/dave/sub2/file2
접두사가 /home/dave
이며 /home/dave
나머지 두 개는 유지되지 않습니다. 즉, 위 명령이 수행하는 작업과 완전히 반대되는 작업을 수행합니다 sed
.
더 많은 정보:
- 입력은 이미 정렬되어 있습니다.
/home/dave /home/dave/file1 /home/phil /home/phil/file2
( ) 가 있다면echo -e '/home/dave\n/home/dave/file1\n/home/dave/sub2/file2\n/home/phil\n/home/phil/file2'
나는 기대/home/dave
하고/home/phil
대답할 것이다.
애플리케이션:
비슷한 콘텐츠가 포함된 두 개의 디스크 볼륨이 있습니다. v1에는 있지만 v2에는 없는 내용을 다른 디스크 볼륨인 v3에 복사하고 싶습니다. find
, sort
및 를 사용하면 comm
복사할 항목의 목록을 얻을 수 있지만 해당 목록을 추가로 정리해야 합니다. 즉, /home/dave
목록에 있는 한 다른 두 개는 필요하지 않습니다.
감사해요!
답변1
이 답변에서는 Python을 사용합니다. OP가 가능하다고 생각한 대로 부모가 보호한 디렉토리를 제거하기를 원했기 때문에 덮개를 제거하기 위한 다른 프로그램을 작성하기 시작했습니다.
예:
$ echo -e '/home/dave\n/home/dave/file1\n/home/dave/sub2/file2\n/home/phil\n/home/phil/file1' | removecoverings
/home/phil
/home/dave
명령 코드 removecoverings
:
#!/usr/bin/env python2
import sys
def list_startswith(a, b):
if not len(a) >= len(b):
return False
return all(x == y for x,y in zip(a[:len(b)],b))
def removecoverings(it):
g = list(it)
g.sort(key=lambda v: len(v.split('/')), reverse=True)
o = []
while g:
c = g.pop()
d = []
for v in g:
if list_startswith(v.split('/'), c.split('/')):
d.append(v)
for v in d:
g.remove(v)
o.append(c)
return o
for o in removecoverings(l.strip() for l in sys.stdin.readlines()):
print o
이 답변에서는 Python을 사용합니다. 또한 문자열 방식의 공통 접두사가 아닌 구성 요소 방식을 수행합니다. /ex/ample
및 의 공통 접두사인 경로에 더 적합 /exa/mple
합니다 . 이는 원하는 것이 가장 큰 공통 접두사이며 덮개가 제거된 접두사 목록이 아니라고 가정합니다. 그보다는 가지고 있고 기대한다면 . 이것은 당신이 찾고 있는 대답이 아닙니다./
/ex
/home/dave /home/dave/file1 /home/phil /home/phil/file2
/home/dave /home/phil
/home
예:
$ echo -e '/home/dave\n/home/dave/file1\n/home/dave/sub2/file2' | commonprefix
/home/dave
명령 코드 commonprefix
:
#!/usr/bin/env python2
import sys
def commonprefix(l):
# this unlike the os.path.commonprefix version
# always returns path prefixes as it compares
# path component wise
cp = []
ls = [p.split('/') for p in l]
ml = min( len(p) for p in ls )
for i in range(ml):
s = set( p[i] for p in ls )
if len(s) != 1:
break
cp.append(s.pop())
return '/'.join(cp)
print commonprefix(l.strip() for l in sys.stdin.readlines())
답변2
입력이 정렬된 경우 의사 코드는 다음과 같습니다.
$seen = last_line;
if current_line begins exactly as $seen then next
else { output current_line; $seen = current_line }
Perl 코드로 번역하기(예, Perl은 가장 아름다운 스크립트 언어입니다):
perl -e '
my $l = "\n";
while (<>) {
if ($_ !~ /^\Q$l/) {
print;
chomp;
$l = $_;
}
}
'
신용 거래:Ben Bacarisse @bsb.me.uk, comp.lang.perl.misc에서. 고마워요 벤, 잘 작동해요!
답변3
그리고 xpt 답변의 한 줄짜리 버전입니다. 이번에도 정렬된 입력을 가정합니다.
perl -lne 'BEGIN { $l="\n"; }; if ($_ !~ /^\Q$l/) { print $_; $l = $_; }'
예제 입력에서 실행
/home/dave
/home/dave/file1
/home/dave/sub2/file2
/home/phil
/home/phil/file2
사용하여
echo -e '/home/dave\n/home/dave/file1\n/home/dave/sub2/file2\n/home/phil\n/home/phil/file2' | perl -lne 'BEGIN { $l="\n"; }; if ($_ !~ /^\Q$l/) { print $_; $l = $_; }'
준다
/home/dave
/home/phil
마법은 Perl의 명령줄 인수에 있습니다. -e
명령줄에서 스크립트를 제공하고, -n
파일의 줄을 반복하고(각 줄을 에 배치 $_
), -l
개행을 처리할 수 있습니다.
스크립트는 l
표시된 마지막 접두사를 추적하는 데 사용됩니다. 블록 BEGIN
은 첫 번째 줄을 읽기 전에 실행되며, 표시되지 않는 문자열(개행 없음)로 변수를 초기화합니다. 조건은 파일의 각 줄에서 실행됩니다( 에 의해 유지됨 $_
). 조건은 파일의 모든 줄에서 실행되며 "줄에 접두사로 현재 값이 없으면 l
줄을 인쇄하고 값으로 저장합니다 l
." 라고 말합니다. 명령줄 인수로 인해 이는 기본적으로 다른 스크립트와 동일합니다.
문제는 두 스크립트 모두 공통 접두사가 자체 줄로 존재한다고 가정하므로 다음과 같은 입력에 대한 공통 접두사를 찾지 않는다는 것입니다.
/home/dave/file1
/home/dave/file2