셸 도구 awk를 사용하여 fslint|query|sed의 출력을 편집합니다.

셸 도구 awk를 사용하여 fslint|query|sed의 출력을 편집합니다.

작업은 이 텍스트 파일(유틸리티의 출력)을 fslint일련의 규칙에 따라 제거할 중복 파일에 대한 명령줄과 보관할 파일에 대한 주석 줄이 포함된 bash 스크립트로 변환하는 것입니다.rm

규칙은 기본적으로 다음과 같습니다. 특정 디렉터리의 중복 파일만 삭제합니다.

목표는 여러 운영 체제(Mac OS X, Windows, Linux)에서 수년간 누적된 약 1TB의 중복을 정리하는 것입니다. 모든 데이터가 Linux 드라이브에 복사되었습니다.

#3 x 697,612,024        (1,395,236,864) bytes wasted
/path/to/backup-100425/file_a.dat
/another/path/to/backup-disk-name/171023/file_a.dat
/yet/another/path/to/labs data/some/path/file_a.dat
#4 x 97,874,344 (293,634,048)   bytes wasted
/path/to/backup-100425/file b.mov
/another/path/to/backup-140102/file b.mov
/backup-120708/Library/some/path/file b.mov
/some/other/path/to/backup-current/file b.mov
#2 x 198,315,112        (198,316,032)   bytes wasted
/path/to/backup-100425/file_c.out
/another/path/to/backup-disk-name/171023/file_c.out

첫 번째 줄은 3개의 동일한 복사본이 있음을 나타내고 file_a.dat다음 3줄은 해당 경로를 나열합니다. 이상적으로는 여기에서 2개의 사본을 삭제해야 합니다. 내가 말하는 것은 6자리(YYMMDD 형식의 날짜)로 구성된 디렉토리입니다.기록 백업 디렉터리.

규칙, 적용이 순서대로동일한 파일의 각 세트는 다음과 같습니다.

  1. 파일이 디렉터리가 포함된 경로에 있으면 Library해당 디렉터리를 유지하세요.
  2. 파일이 labs data또는 에 있는 경우 backup-current파일을 유지하고 그 안에 있는 모든 중복 항목을 제거합니다.기록 백업 디렉터리.
  3. 파일이 기록 백업 디렉터리에 있는 경우 파일을 최신 백업 디렉터리에 보관하고 이전 중복 항목을 삭제합니다.
  4. 그렇지 않으면 파일을 보관하십시오.

원하는 출력은 다음과 같습니다.

#!/bin/bash
#3 x 697,612,024        (1,395,236,864) bytes wasted
rm '/path/to/backup-100425/file_a.dat'
rm '/another/path/to/backup-disk-name/171023/file_a.dat'
#/yet/another/path/to/labs data/some/path/file_a.dat
#4 x 97,874,344 (293,634,048)   bytes wasted
rm '/path/to/backup-100425/file b.mov'
rm '/another/path/to/backup-140102/file b.mov'
#/backup-120708/Library/some/path/file b.mov
#/some/other/path/to/backup-current/file b.mov
#2 x 198,315,112        (198,316,032)   bytes wasted
rm '/path/to/backup-100425/file_c.out'
#/another/path/to/backup-disk-name/171023/file_c.out

나는 쉘 도구 awk, grep 및 sed에 익숙하지 않습니다.이 스레드나는 나의 첫 번째 초안이 개념적으로 잘못되었다는 것을 깨달았습니다. "그것은 [내가] C와 같은 명령형 언어에서 할 일을 순진하게 번역한 것이었습니다."

사실 여기서 다루고 있는 내용은 다음과 같습니다.문서, 하지만 함께파일의 내용.

이 상황에 적합한 쉘 스크립트를 사용하고 있습니까?
그렇다면 효율적인 스크립트는 어떤 모습일까요?

편집하다:@Ed의 답변과 코드를 읽은 후 작업과 요구 사항을 명확히 하려고 노력했고 이로 인해 문제가 완벽하게 해결되었습니다.

답변1

내가 얼마나 많은 시간을 투자할 의향이 있는지를 고려하면 귀하의 요구 사항 목록을 이해할 수 없습니다. 그러나 다음은 귀하가 관심 있어 보이는 파일 형식을 정렬하고 인쇄하기 위한 스크립트입니다. 나머지는 귀하가 알아내실 수 있기를 바랍니다.

$ cat tst.awk
/^#/ { prt(); print; next }
{ files[$0] }
END { prt() }

function prt(   file, isLibrary, isLabsBack, isNothing) {
    for (file in files) {
        if ( file ~ /(^|\/)Library(\/|$)/ ) {
            isLibrary[file]
        }
        else if ( file ~ /(^|\/)(labs data|backup-current)(\/|$)/ ) {
            isLabsBack[file]
        }
        else {
            isNothing[file]
        }
    }
    for (file in isLibrary) {
        print "Library", file
    }
    for (file in isLabsBack) {
        print "LabsBack", file
    }
    for (file in isNothing) {
        print "Nothing", file
    }
    delete files
}

.

$ awk -f tst.awk file
#3 x 697,612,024        (1,395,236,864) bytes wasted
LabsBack /yet/another/path/to/labs data/some/path/file_a.dat
Nothing /another/path/to/backup-disk-name/171023/file_a.dat
Nothing /path/to/backup-100425/file_a.dat
#4 x 97,874,344 (293,634,048)   bytes wasted
Library /backup-120708/Library/some/path/file b.mov
LabsBack /some/other/path/to/backup-current/file b.mov
Nothing /path/to/backup-100425/file b.mov
Nothing /another/path/to/backup-140102/file b.mov
#2 x 198,315,112        (198,316,032)   bytes wasted
Nothing /path/to/backup-100425/file_c.out
Nothing /another/path/to/backup-disk-name/171023/file_c.out

답변2

관심 있는 사람들을 위해 질문에 언급된 원하는 출력을 제공하는 코드는 다음과 같습니다. 이것은 @Ed의 정말 스마트한 코드를 약간만 적용한 것입니다.

BEGIN { print "#!/bin/bash" }
/^#/ { prt(); print; next }
{ files[$0] }
END { prt() }

function prt(   file, isDate, isKeep, isDelete, backup, latest, pats) {
    # file exists in a current backup directory (yes|no)
    backup = "no"
    # latest historical backup date
    latest = "000000"
    for (file in files) {
        if ( file ~ /\/Library\// ) {
            # files to check manually
            isKeep[file]
        }
        else if ( file ~ /\/(labs data|backup-current)\// ) {
            # backup files to keep
            isKeep[file]
            backup = "yes"
        }
        else if ( match(file, /\/(backup-disk-name\/|backup-)([0-2][0-9][0-1][0-9][0-3][0-9])\//, pats) != 0 ) {
            # files in historical backup directories
            if ( pats[2] > latest ) {
                latest = pats[2]
            }
            isDate[file] = pats[2]
        }
        else {
            # unclassified files to check manually
            isKeep[file]
        }
    }
    for (file in isDate) {
        if ( isDate[file] == latest && backup == "no") {
            isKeep[file]
        }
        else {
            isDelete[file]
        }
    }
    for (file in isKeep) {
        print "#", file
    }
    for (file in isDelete) {
        # use single quotes to escape special characters in file
        # use gensub() to escape single quotes in file
        print "rm", "'" gensub(/'/,"'\\\\''", "g", file) "'"
    }
    delete files
}

마지막으로 몇 가지 생각을 공유하고 싶습니다. 너무 멀리 벗어나지 않았으면 좋겠습니다.
몇 주 전에 저는 마침내 그 거대한 백업을 정리하기로 결정했습니다(일부 파일에는 10개 이상의 중복이 있었습니다). 하지만 이 작업을 자동화하는 도구를 찾을 수 없습니다. 나는 이것을 위해 C 프로그램을 시작하고 싶지도 않고 Perl 방식으로 하고 싶지도 않습니다. 그래서 나는 쉘 루트로 가야 한다는 것을 알았습니다. 하지만 어디서부터 시작해야 할지 모르겠습니다. 첫 번째 줄에 갇혔습니다.

많이 읽은 후에도 여전히 혼란 스럽습니다. 그래서 SE에 질문을 게시하기로 결정했습니다.
@Ed의 코드를 처음 읽었을 때 "대체 뭐지!"라고 생각했습니다. 그러다가 그것을 받았을 때 그것이 효율적이고 명확한 훌륭한 코드라는 것을 깨달았습니다.

그래서 여기 있습니다. 약 일주일 전에 나는 awkRegExp에 대해 아무것도 모르고 거의 알지 못했습니다. 이제 @Ed의 기여 덕분에 "나의" 첫 번째 awk스크립트를 작성하고 RegExp 세계를 더 잘 이해하고 당면한 작업을 완료할 수 있었습니다 . 더 중요한 것은 이제 RegExp awk및 기타 텍스트 처리 셸 도구를 직접 살펴볼 만큼 자신감이 생겼다는 것입니다 . 이는 또한 제가 SE에 더 많이 기여하도록 동기를 부여합니다.
저처럼 산을 마주하는 등 어려운 상황에 처해 있는 분들에게 희망을 주고자 제 개인적인 경험을 공유하고 싶었습니다.

관련 정보