정렬된 경로 목록이 포함된 텍스트 파일이 있는 경우 해당 상위 경로(직접 여부와 상관없이)도 목록에 있기 때문에 중복되는 모든 경로를 제거하려면 어떻게 해야 합니까?
예를 들어:
/aaa/bbb
/aaa/bbb/ccc
/ddd/eee
/fff/ggg
/fff/ggg/hhh/iii
/jjj/kkk/lll/mmm
/jjj/kkk/lll/mmm/nnn
다음과 같이 줄여야 합니다.
/aaa/bbb
/ddd/eee
/fff/ggg
/jjj/kkk/lll/mmm
awk에서 하위 문자열을 사용해 보았지만 상위 경로가 매번 동일한 수준에 있을 것이라고 보장할 수 없으므로 제대로 작동할 수 없습니다.
답변1
나는 이것이 가능해야 한다고 생각한다. 더 많은 사례를 추가하려면 입력 파일을 수정하세요.
$ cat ip.txt
/aaa/bbb
/aaa/bbbd
/aaa/bbb/ccc
/ddd/eee
/fff/ggg
/fff/ggg/hhh/iii
/jjj/kkk/lll/mmm
/jjj/kkk/lll/mmm/nnn
/jjj/kkk/xyz
사용awk
$ awk '{for (i in paths){if (index($0,i"/")==1) next} print; paths[$0]}' ip.txt
/aaa/bbb
/aaa/bbbd
/ddd/eee
/fff/ggg
/jjj/kkk/lll/mmm
/jjj/kkk/xyz
paths[$0]
입력 행에 의해 키가 지정된 참조입니다.for (i in paths)
각 행은 저장된 모든 키와 비교됩니다.if (index($0,i"/")==1) next
/
줄 시작 부분에 추가된 저장된 키와 일치하는 경우 입력 줄을 건너뜁니다./
/aaa/bbbd
매칭을 피하기 위해 사용됨/aaa/bbb
답변2
그리고 강제 sed
솔루션은 다음과 같습니다.
sed '1s/^/#/;x;G;\_#\([^#]*\)#.*\n\1/_s/\n.*//;s/\n\(.*\)/\1#/;h;$! d;x;s/^#//;s/#$//;y/#/\n/'
이 스크립트는 예약된 공간에서 경로를 수집합니다. 각각의 새 라인에 대해 예약된 공간이 패턴 공간에 추가되어 발생 여부를 확인합니다.
이 솔루션에서는 #
해당 문자가 파일에 사용되지 않는다고 가정합니다. 그렇지 않으면 다른 문자를 사용하거나, GNU를 사용하는 경우 sed
게시물 하단에 있는 짧은 버전을 사용하세요.
상해:
1s/^/#/
이식성을 위해 #
예약된 공간에서 경로를 구분하는 데 문자가 사용됩니다. 첫 번째 줄의 경우 처음부터 시작해야 합니다.#
x;G
By exchanging the spaces and appending the hold space, we have the list of already occured buffers first, then the new path.
\_#\([^#]*\)#.*\n\1/_s/\n.*//
주소가 일치 하면 \_..._
새 경로는 이전 경로의 하위 경로이므로 삭제됩니다.
s/\n\(.*\)/\1#/
여전히 공백에 줄 바꿈이 있으므로 경로가 새 경로이므로 목록에 추가합니다.
h;$! d
마지막 행이 아닌 경우 새 목록을 예약된 공간에 저장하고 다시 시작하세요.
x;s/^#//;s/#$//;y/#/\n/
마지막 줄의 시작과 끝 내용을 삭제하고 #
다른 내용을 줄바꿈으로 바꿉니다.#
GNU의 대안sed
sed
순서가 복원되어도 괜찮다면 GNU 확장을 사용하여 이 작업을 더 간단하게 수행할 수 있습니다.
sed 'G;\_^\([^\n]*\)/.*\n\1\n_s/[^\n]*\n//;h;$! d;x;s/^\n//;s/\n$//'
위와 같이 설명했지만 #
.
답변3
이 같은:
$ awk '{sub(/\/$/, "")}
NR != 1 && substr($0, 0, length(prev)) == prev {next};
{print; prev = $0"/" } ' paths
첫 번째( NR != 1
)를 제외한 모든 줄에서 해당 줄의 접두어를 저장된 줄 prev
( 길이만큼의 문자 prev
)과 비교합니다. 일치하면 next
해당 행으로 이동합니다. 그렇지 않으면 print
출력하고 행을 에 저장합니다 prev
.
파일이 C 로케일로 정렬되어 있다고 가정하면(예: /
문자 앞), 또는 디렉토리 트리 탐색을 통해 생성된 경우 이전에 저장된 행에 대해 테스트하는 것으로 충분합니다. 파일이 다른 로케일로 정렬된 경우 /
정렬에 영향을 주지 않아 , , 와 같은 정렬이 발생할 수 /aaa/bbb
있습니다 /aaaccc
. /aaa/ddd
파일이 전혀 정렬되지 않으면 하위 디렉터리가 상위 디렉터리 앞에 나타날 수 있으며 문제가 어려워집니다.
첫 번째는 sub(...)
줄에서 후행 슬래시(있는 경우)를 제거합니다. 줄을 저장할 때 파일 이름의 일부가 일치하지 않도록 후행 슬래시를 추가합니다.
답변4
perl -lne '$l=$_; grep $l =~ m|^\Q$_/|, @A or print, push @A, $_'
- 특정 행에 제공된 다양한 경로를 모두 누적했는데
array @A
해당 행에 이미 저장된 경로와 일치하지 않습니다. - grep은
m|^\Q$_/|
배열 요소를 참조하고 일치하는 항목을 찾습니다.
sed -ne '
H # append current line into hold space
g # pattern space = hold space \n current line
y/\n_/_\n/ # change coordinate system
\|_\([^_]*\)_\(.*_\)\{0,1\}\1/|s/\(.*\)_.*/\1/ # match yes, strip current line
y/\n_/_\n/ # revert coordinate system
h # update hold space
$s/.//p # answer
'
산출
/aaa/bbb
/ddd/eee
/fff/ggg
/jjj/kkk/lll/mmm