파일 시작 부분에서 줄 추출

파일 시작 부분에서 줄 추출

나는 임무가 있습니다:

현재 디렉터리에 있는 모든 ".c" 파일의 처음 3줄 코드를 위치 인수로 제공된 임시 파일에 복사하는 쉘 스크립트를 작성하세요. 임시 파일의 내용을 표시합니다.

처음에 ".c"로 끝나는 파일을 생성한 다음 head -3 *.c> touch $1해당 확장자를 가진 모든 파일의 처음 3줄을 복사할 수 있었지만 올바르게 수행하고 있는지 또는 문제를 해결할 수 있는지 알고 싶습니다. 다른 방식으로는.

답변1

head언뜻 보면 방법이 좋아 보이지만 head동시에 여러 파일을 실행할 때 유틸리티가 파일 이름이 포함된 출력에 헤더를 넣는 방법을 알 수 있습니다.

아마도 당신은가능한이 제목을 받지 않으려면 과제 텍스트를 따르세요.

touch전혀 사용할 필요도 없습니다. 나는 다른 숙제를 해결하려고 할 때 사람들이 때때로 "파일을 생성"한 다음 이 작업을 수행하는 데 사용하는 해당 파일로 데이터를 리디렉션해야 한다고 생각하는 것을 발견했습니다 touch.

리디렉션을 사용할 때 >filename해당 파일이 아직 존재하지 않는 경우 해당 파일이 자동으로 생성됩니다(현재 디렉터리의 권한이 허용하는 경우). 파일이 존재하는 경우 잘립니다(비어집니다).

head좋습니다. 출력에서 ​​파일 이름이 포함된 헤더 생성을 어떻게 중지합니까 ? 음, Linux 시스템을 사용하고 있다면 아마도 GNU가 있을 것입니다 head. 이 구현 head에는비표준 -q옵션제목을 억제합니다.

따라서 스크립트는 다음과 같이 작성할 수 있습니다.

#!/bin/sh

head -q -n 3 -- *.c >"$1"

...사용자가 GNU 를 가지고 있다고 가정합니다 head. 파일 이름 와일드카드 패턴과 일치하는 파일 이름 이 대시로 시작하는 --경우 "명령줄 옵션 끝" 신호를 내보내야 합니다 . 대시는 옵션 문자열의 시작으로 간주될 수 있습니다.head*.c

또 다른 방법은 현재 디렉터리에서 파일을 명시적으로 참조할 head -q -n 3 ./*.c위치를 사용하는 것입니다 . ./*.c모든 파일 이름은 대시로 시작하므로 ./인수가 대시로 시작할 가능성이 없으므로 --더 이상 필요하지 않습니다. 이를 수행하는 방법은 귀하에게 달려 있지만 --help.c현재 디렉토리( ) touch -- --help.c에 있는 파일을 사용하여 스크립트를 테스트하십시오 .

/bin/sh대신 스크립트의 인터프리터를 사용하기로 결정했습니다 /bin/bash. 스크립트가 배열, 프로세스 대체, 중괄호 확장, 정규식 일치 등과 같은 bash필요한 것을 사용하지 않았기 때문입니다.bash

Linux 시스템을 사용하지 않거나 POSIX 표준을 따르고 작성하려는 경우가지고 다닐 수 있는-q스크립트 와 함께 사용하면 안 됩니다 head.

대신 파일을 반복하여 head각 개별 파일에 사용할 수 있습니다.

#!/bin/sh

for name in *.c; do
    head -n 3 -- "$name"
done >"$1"

출력을 어떻게 리디렉션하는지 확인하세요.회보파일로.

또한 루프를 사용하여 이 문제를 해결하면 다음과 같은 경우에도 스크립트가 제대로 작동한다는 것을 알 수 있습니다.수천파일 수 .c. 루프가 없으면 head쉘이 수천 개의 파일 이름 확장을 모두 사용하여 실행하려고 할 때 "인수 목록이 너무 김" 오류가 발생할 수 있습니다. 이것결점head한 가지 문제는 특히 수천 개의 파일이 있는 경우 각 파일을 개별적으로 대상으로 지정하는 것이 매우 느리다는 것입니다 .

다음 문제는 스크립트 사용자가 올바른 매개변수를 제공하지 않으면 어떤 일이 발생하는지 파악하는 것입니다. 사용자가 이미 존재하는 파일 이름을 사용하거나 파일 이름이 전혀 없는 상태에서 이 스크립트를 실행한다고 가정합니다. 아무것도 하지 않고 이것을 잡고 불평합시다:

#!/bin/sh

if [ "$#" -ne 1 ]; then
    printf 'expecting 1 argument, got %d\n' "$#" >&2
    exit 1
elif [ -e "$1" ]; then
    printf 'the name "%s" already exist, refusing to over-write\n' "$1" >&2
    exit 1
fi

for name in *.c; do
    head -n 3 -- "$name"
done >"$1"

이는 if스크립트에 제공된 명령줄 인수 수를 먼저 테스트하는 명령문을 소개합니다. 정확히 하나가 아니라면 불평하고 그만 두십시오. 하나인데 이미 존재하는 이름을 가리키는 경우 불평하고 종료하십시오.

진단 메시지(예: 오류)는 표준 오류 스트림에 기록되어야 합니다. 여기서는 리디렉션된 출력을 사용하여 이 작업을 수행합니다 >&2. 또한 계속할 수 없는 것이 분명한 경우 0이 아닌 종료 상태로 스크립트를 종료합니다. 이를 통해 스크립트가 성공적으로 실행되는지 테스트할 수 있습니다.

if ./your-script.sh hello world; then
    echo ok
else
    echo something went wrong
fi

남은 문제는 다음 상황을 처리하는 것입니다.아니요 .c현재 디렉터리의 파일입니다. 이런 일이 발생하면 스크립트가 어떻게 이상한 오류를 생성하는지 알 수 있습니다.

head: *.c: No such file or directory

와 같은 패턴이 *.c아무것도 일치하지 않으면 확장되지 않은 상태로 유지되기 때문입니다. 루프에 작은 테스트를 추가하여 이 문제를 해결할 수 있습니다.

for name in *.c; do
    [ ! -e "$name" ] && continue
    head -n 3 -- "$name"
done >"$1"

이는 "파일이 $name존재하지 않으면 이 루프 반복을 건너뜁니다"를 의미합니다.

bash이것을 스크립트 로 작성하면 원래 루프를 유지한 다음 nullglob루프 앞에 셸 옵션을 설정하여 shopt -s nullglob셸이 일치하지 않는 패턴을 확장되지 않은 상태로 두는 대신 제거하도록 할 수 있습니다.

이제 스크립트를 실행하면 .c현재 디렉터리에 파일이 없을 때 오류가 생성되지 않습니다. 그러나 이런 일이 발생하면 출력 파일은 비어 있게 됩니다. 이것이 바람직하지 않은 경우 *.c루프를 시작하고 출력 파일로 리디렉션하기 전에 실제로 일치하는 항목이 있는지 테스트 할 수 있습니다 .

outfile=$1

set -- *.c

if [ -e "$1" ]; then
   for name do
       head -n 3 -- "$name"
   done >"$outfile"
fi

여기서는 위치 매개변수(스크립트에 제공된 인수)를 모든 파일의 이름으로 재정의하므로 먼저 출력 파일의 이름을 별도의 변수에 저장합니다 .c(이 작업은 를 통해 수행됨 set).

첫 번째 위치 인수가 기존 파일 이름인 경우 루프가 실행되고 출력 파일을 생성/자르도록 허용합니다.

루프에 반복할 항목이 없으면(마지막 코드 부분에 표시된 대로) 기본적으로 위치 인수 목록이 반복됩니다. 우리는 로 시작하는 이름으로 이 목록을 설정했습니다 .c.


다른 변형은 head전혀 사용하지 않고 또는 또는 다른 명령과 같은 동등한 명령을 사용하는 것 sed 3q입니다 awk '1; NR == 3 { exit }'. 그러나 이 유틸리티를 사용하면 head수행하려는 작업에 대한 명확한 아이디어를 얻을 수 있습니다.

관련 정보