머리와 꼬리를 사용하여 다른 선 세트를 잡고 동일한 파일로 저장 다른 파일에 저장하십시오. 나는 다음과 같은 추가를

그래서 이것은 숙제를위한 것이지만 구체적인 숙제에 대해서는 묻지 않을 것입니다.

하나의 파일에서 다른 라인 세트를 가져 오기 위해 head와 tail을 사용해야합니다. 따라서 6-11 행과 19-24 행과 같이 두 파일을 다른 파일에 저장하십시오. 나는 다음과 같은 추가를 사용하여 이것을 할 수 있다는 것을 안다.

head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.

그러나 나는 우리가해야한다고 생각하지 않습니다.
head 및 tail 명령을 결합한 다음 파일에 저장할 수있는 특정 방법이 있습니까?



답변

다음과 같은 구문 head{ ... ; }사용하여 명령을 그룹화하면 단독 및 기본 산술로 수행 할 수 있습니다

{ head -n ...; head -n ...; ...; } < input_file > output_file

모든 명령이 동일한 입력을 공유합니다 ( @mikeserv 덕분에 ).
6-11 행과 19-24 행을 얻는 것은 다음과 같습니다.

head -n 5 >/dev/null  # dump the first 5 lines to `/dev/null` then
head -n 6             # print the next 6 lines (i.e. from 6 to 11) then
head -n 7 >/dev/null  # dump the next 7 lines to `/dev/null` ( from 12 to 18)
head -n 6             # then print the next 6 lines (19 up to 24)

따라서 기본적으로 다음을 실행합니다.

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } < input_file > output_file

답변

{ … }그룹화 구문을 사용하여 경로 재 지정 연산자를 복합 명령에 적용 할 수 있습니다 .

{ head -n 11 file | tail -n 6; head -n 24 file | tail -n 6; } >file1

첫 번째 M + N 행을 복제하고 마지막 N 만 유지하는 대신 첫 번째 M 행을 건너 뛰고 다음 N을 복제 할 수 있습니다. 이는 큰 파일에서 훨씬 빠릅니다 . 것을주의 +N의 인수가 tail스킵하는 라인의 수 있지만, 하나가 아닌 플러스 있음 – 1부터 번호 라인을 인쇄 할 첫 번째 행의 수입니다.

{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1

어느 쪽이든, 출력 파일은 한 번만 열리지 만 각 스 니펫에서 추출 할 때마다 입력 파일이 한 번 순회됩니다. 입력을 그룹화하는 것은 어떻습니까?

{ tail -n +6 | head -n 6; tail -n +14 | head -n 6; } <file >file1

일반적으로 이것은 작동하지 않습니다. (적어도 입력이 일반 파일 인 경우 일부 시스템에서 작동 할 수 있습니다.) 왜 그렇습니까? 입력 버퍼링 때문에 . 를 포함한 대부분의 프로그램 tail은 입력을 바이트 단위로 읽지 않지만 한 번에 몇 킬로바이트를 읽습니다. 속도가 빠르기 때문입니다. 따라서 tail몇 킬로바이트를 읽고 시작 부분을 조금 건너 뛰고 조금 더 지나간 다음 head중지합니다. 그러나 읽은 내용은 읽히고 다음 명령에는 사용할 수 없습니다.

또 다른 방법head파이프 를 사용 /dev/null하여 줄을 건너 뛰는 것입니다.

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } <file >file1

다시 말하지만 버퍼링으로 인해 작동하지 않을 수 있습니다. head입력이 일반 파일에서 나올 때 GNU coreutils (비 내장 Linux 시스템에 있는 명령) 의 명령 으로 작동 합니다. 이 구현이 head원하는 것을 읽은 후에는 파일 위치 를 출력하지 않은 첫 바이트로 설정하기 때문입니다. 입력이 파이프 인 경우 작동하지 않습니다.

파일에서 여러 줄의 행을 인쇄하는 간단한 방법은 sed 또는 awk 와 같은보다 일반적인 도구를 호출하는 것 입니다. (이 속도는 느려질 수 있지만 매우 큰 파일에만 중요합니다.)

sed -n -e '6,11p' -e '19,24p' <file >file1
sed -e '1,5d' -e '12,18d' -e '24q' <file >file1
awk '6<=NR && NR<=11 || 19<=NR && NR<=24' <file >file1
awk 'NR==6, NR==11; NR==19, NR==24' <file >file1

답변

나는 당신이 머리와 꼬리를 사용해야한다고 말했지만 sed는 분명히 작업을위한 가장 간단한 도구입니다.

$ cat foo
a 1 1
a 2 1
b 1 1
a 3 1
c 3 1
c 3 1
$ sed -ne '2,4p;6p' foo
a 2 1
b 1 1
a 3 1
c 3 1

다른 프로세스를 사용하여 문자열로 블록을 작성하고 sed를 통해 실행할 수도 있습니다.

$ a="2,4p;6p"
$ sed -ne $a foo
a 2 1
b 1 1
a 3 1
c 3 1

-n은 출력을 무시한 다음 범위의 첫 번째 숫자와 마지막 숫자를 쉼표로 구분하여 p로 인쇄 할 범위를 지정합니다.

즉, @don_crissti가 제안한 명령 그룹화를 수행하거나 머리 / 꼬리로 갈 때마다 줄 덩어리를 잡아서 파일을 몇 번 반복 할 수 있습니다.

$ head -4 foo | tail -3; head -6 foo | tail -1
a 2 1
b 1 1
a 3 1
c 3 1

파일에 줄이 많고 블록이 많을수록 sed가 더 효율적입니다.


답변

로에게 sed당신이 할 수 있습니다 :

sed '24q;1,5d;12,18d' <infile >outfile

… 아마도 더 효율적인 솔루션이있을 수 있습니다 head. Don은 이미 그 방법이 잘 작동하는 방법을 이미 보여 주었지만, 나는 그것도 가지고 놀았습니다. 이 특정한 경우를 처리하기 위해 할 수있는 일 :

for   n in 5 6 7 6
do    head -n"$n" >&"$((1+n%2))"
done  <infile >outfile 2>/dev/null

… 반복의 값 이 짝수인지 홀수 인지 에 따라 head4 번이나 호출 합니다 .outfile/dev/null$n

더 일반적인 경우에는 이미 가지고있는 다른 것들과 함께 이것을 조롱했습니다.

somehead()(
### call it like:
### somehead -[repeat] [-][numlines]* <infile >outfile
    set -e -- "${1#-}" "$@"                             #-e for arg validation
    r=; cd -- "${TMP:-/tmp}"                            #go to tmp
    dd bs=4096 of="$$$$" <&4 2>&3 &                     #dd <in >tmpfile &bg
    until [ -s "$$$$" ]; do :; done                     #wait while tmpfile empty
    exec <"$$$$" 4<&-;   rm "$$$$"                      #<tmpfile; rm tmpfile
    [ "$3${1}0" -ne "$3${2#?}0" ]          ||           #validate args - chk $1
            shift "$(((r=-${1:--1})||1))"; shift        #shift 1||2
    while [ "$(((r+=(_n=1))-1))" -ne 0 ]   &&           #while ! $rptmax &&
          IFS= read -r l                   &&           #      ! EOF     &&
          printf "%.$(($1>0?${#l}+1:0))s" "$l           #      ? printf  do
";  do    for n do [ "${n#-}" -gt 0 ]      || exit      #args all -[nums>0]
          head "-n$((${n#-}-_n))" >&"$((n>(_n=0)?1:3))" #head -n?$1 >?[+-]
    done; done                                          #done and done
)   4<&0 3>/dev/null                                    #4<for dd 3>for head

이것은 다음과 같은 일을 할 수 있습니다.

 seq 100 | somehead -1 -5 6 -7 6

… 인쇄 …

6
7
8
9
10
11
19
20
21
22
23
24

첫 번째 인수는 접두사로 시작하는 반복 횟수 -, 또는 실패한 경우에만 a -입니다. 카운트가 제공되면 지정된 횟수만큼 다음 args에 주어진 라인 패턴을 반복하고 그렇게되면 중지합니다.

다음에 나오는 각 인수에 대해 기록해야하는 행 수를 나타 내기 위해 음의 정수를 해석하고에 기록되어야 /dev/null하는 행 수를 나타 내기 위해 양의 정수를 해석합니다 stdout.

따라서 위의 예에서는 처음 5 행을 /dev/null다음에 6 stdout, 다음 7을 /dev/null다시, 다음 6을 다시 한 번에 인쇄합니다 stdout. 마지막 인수에 도달하고 -1반복 횟수를 완전히 순환 하면 종료됩니다. 첫 번째 인수가 -2있었다면 프로세스를 한 번 더 반복했거나 -가능한 한 오랫동안 진행했을 것입니다.

각 인수주기마다 while루프가 한 번 처리됩니다. 각 루프의 맨 위에서 첫 번째 줄부터 stdin쉘 변수를 읽습니다 $l. 이것은 while head </dev/null; do :; done무한정 반복 되기 때문에 필요 head합니다. 파일 끝에 도달하면 리턴으로 표시합니다. EOF에 대한 확인을하기 위해 최선을 다하고 있습니다 있도록 read하고 printf기록합니다 $l에 플러스 바꿈을 stdout두 번째 인수는 양의 정수 경우에만.

read검사는 다른 루프가 호출 된 직후에 루프를 약간 복잡하게 만듭니다. 루프 는 상위 루프 의 각 반복 에 대해 표시된대로 forargs 2-$#$n반복 while합니다. 즉, 각 반복에 대해 첫 번째 arg는 명령 행에 지정된 값에서 하나씩 감소해야하지만 다른 모든 값은 원래 값을 유지해야하므로 $_n마커 var의 값을 각 값에서 빼야합니다. 첫 번째 인수의 값이 0보다 큽니다.

이는 함수의 주요 루프를 구성하지만 대부분의 코드는 맨 위에 있으며 함수를 파이프로 입력으로 깔끔하게 버퍼링 할 수 있도록 고안되었습니다. 이것은 먼저 backgrounded dd를 호출하여 블록 크기가 4k 인 출력에서 ​​tmpfile에 복사합니다. 그런 다음 함수는 홀드 루프를 설정합니다. 단일 루프에서도 거의 완료되지 않아야 dd합니다. 함수보다 먼저 파일에 단일 쓰기를 한 다음 stdin을 tmpfile에 연결된 파일 설명 자로 바꿉니다. 그 후 즉시 파일을 연결 해제합니다rm. 이것은 함수가 트랩을 요구하지 않고 또는 정리를 위해 스트림을 안정적으로 처리 할 수있게합니다. 함수가 fd에서 클레임을 해제하자마자 명명 된 파일 시스템 링크 만 이미 제거되었으므로 tmpfile의 존재가 중지됩니다.


답변

다음과 같이 bash 함수를 사용하십시오.

seq 1 30 > input.txt
f(){ head $1 input.txt | tail $2 >> output.txt ;}; f -11 -2; f -24 -3
cat output.txt
10
11
22
23
24

이 경우 약간 과잉이지만 필터가 커지면 이익이 될 수 있습니다.