파일에서 가장 긴 줄을 인쇄하는 방법?


34

파일에서 가장 긴 줄을 인쇄하는 가장 간단한 방법을 찾고 있습니다. 나는 인터넷 검색을했는데 놀랍게도 대답을 찾지 못하는 것 같습니다. 파일에서 가장 긴 줄의 길이를 자주 인쇄하지만 실제로 가장 긴 줄을 인쇄하는 방법을 모르겠습니다. 누구든지 파일에서 가장 긴 줄을 인쇄하는 솔루션을 제공 할 수 있습니까? 미리 감사드립니다.


1
"가장 긴"줄이 여러 개인 경우는 어떻습니까? 단순한 최대 길이 이상을 원하기 때문에 가장 긴 행의 모든 ​​인스턴스를 보시겠습니까?
Peter.O

답변:


38
cat ./text | awk ' { if ( length > x ) { x = length; y = $0 } }END{ print y }'

UPD : 의견에 모든 조언을 요약

awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text 

3
다른 명령 ( cat)을 호출 하고 파이프를 사용하는 것은 비용이 많이 드는 작업이지만 awk가 파일을 읽는 것이 더 효율적이라는 것은 말할 것도 없습니다. 이 작업을 자주 수행하면 성능에 미치는 영향이 눈에 띄게 나타납니다 cat.
Chris Down

7
@laebshade 절대 이유가 있습니다. 따라서 어떤 명령이 파일 이름을 사용하고 어떤 명령을 수행하지 않는지를 기억하거나 파이프 라인에서 어떤 명령이 먼저 실행 될지 신경 쓰지 않아도됩니다. 자주 실행되는 스크립트를 작성하려는 경우 반드시 이와 같은 것에 대해 걱정하십시오. 파일에서 가장 긴 줄을 찾기 위해 일회성 항목을 작성하는 경우 추가 프로세스와 소비되는 시간이 완전히 관련이 없습니다. 그것은 매우 사소한의 사람들이 여기에 집착 것을 바보
마이클 Mrozek

4
@Keith Thompson : cat여기서는 쓸모가 없습니다. 컴퓨터에는 쓸모가 없지만 사람에게는 가치가 있습니다. 첫 번째 변형은 입력을 명확하게 보여줍니다. 흐름이 더 자연 스럽습니다 (왼쪽에서 오른쪽으로). 두 번째 경우에는 창을 스크롤하지 않으면 입력이 무엇인지 알 수 없습니다.
jfs

1
@JFSebastian 왼쪽에서 원하더라도 필요하지 않습니다 cat. < file command잘 작동합니다.
Chris Down

3
@JFSebastian : 명령의 시작 부분에 리디렉션을 작성할 수 있다는 사실은 다소 모호합니다. 내가 시도한 모든 쉘에서 < filename command와 같습니다 filename < command. 그러나 일단 알고 있으면 데이터 흐름의 방향을 명확하게 보여주는 긴 파이프를 작성할 때 (추가 명령을 호출하지 않고) 파이프를 활용할 수 있습니다.< input-file command1 | command2 | command3 > output-file
Keith Thompson

6
cat filename | awk '{ print length }' | sort -n | tail -1

+1 이것에 대한 흥미로운 해결책이 많이 있었지만 이것이 가장 간단했습니다. (이것은 awk가 파일을 읽게함으로써 고양이없이 더 간단하지만 왜
떨리는가

5
sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file

먼저 명령 대체 내부의 파일을 읽고 가장 긴 줄의 길이를 출력합니다 (이전 expand의 의미를 극복하기 위해 탭을 공백으로 변환 함 wc -L-줄의 각 탭 은 1 에서 줄 길이 대신 8 을 추가합니다 ). 그런 다음이 길이는 sed"이 문자 수만큼 줄을 찾아서 인쇄 한 다음 종료"를 의미 하는 표현식에 사용됩니다 . 따라서 이것은 가장 긴 줄이 파일의 상단에 가까울수록 최적 일 수 있습니다.

다른 하나는 sed보다 먼저 생각했습니다 (bash).

#!/bin/bash
while read -r line; do
    (( ${#line} > max )) && max=${#line} && longest="$line"
done
echo "$longest"

2
이 방법은 매우 비싸고 느립니다.
Chris Down

2
@Chris Down : 아 맞습니다. 그러나 문제는 가장 효율적인 방법이 아니라 정렬 방법에 관한 것이 었습니다. 그래도 중소 규모의 파일이나 중요하지 않은 작업에는 적합합니다.
ata

3
경고 : wc의 옵션 -L, --max-line-length은 맨 페이지에 따라 가장 긴 줄의 길이를 인쇄하지만, 더 잘못 파고 들거나 ( 예기치 않은 결과가 나올 때와 같이 ),이 옵션은 1 개의 탭 문자 마다 길이가 8 씩 증가합니다. 이 참조 유닉스 및 리눅스 Q / A\x09
Peter.O

추신. 모든 아마 인 "똑같이 긴"라인, 출력 할 것이다 당신의 대답은 좋은의 ... 것은 강제로 화장실 탭 당 1 문자,이 작품을 계산. sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
Peter.O

1
read line백 슬래시 이스케이프 된 문자를 리터럴 문자로 해석합니다 (예 : \Aresloves to A). 물론 실제 바이트 사용량보다 짧은 시간을 효과적으로보고합니다.이 이스케이프 된 해석 을 방지하려면 다음을 사용하십시오 read -r line.. . . . 또한, 만드는 나오지 + 화장실 버전이 첫 번째 "긴 라인"후 종료, 변경 p{p;q}..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Peter.O

4

Perl 솔루션은 다음과 같습니다.

perl -e 'while(<>){
           $l=length;  
           $l>$m && do {$c=$_; $m=$l}  
         } print $c' file.txt 

또는 가장 긴 줄을 모두 인쇄 하려면

perl -e 'while(<>){
           $l=length;
           push @{$k{$l}},$_;
           $m=$l if $l>$m;
         } print @{$k{$m}}' file.txt 

더 나은 작업이 없었기 때문에 625M 텍스트 파일에서 일부 벤치 마크를 실행했습니다. 놀랍게도 내 Perl 솔루션은 다른 솔루션보다 지속적으로 빠릅니다. 물론 허용되는 awk솔루션 과의 차이 는 작지만 존재합니다. 분명히 여러 줄을 인쇄하는 솔루션은 느리므로 유형별로 정렬하여 가장 빠르거나 느립니다.

가장 긴 줄 중 하나만 인쇄하십시오.

$ time perl -e 'while(<>){
           $l=length;  
           $l>$m && do {$c=$_; $m=$l}  
         } print $c' file.txt 
real    0m3.837s
user    0m3.724s
sys     0m0.096s



$ time awk 'length > max_length { max_length = length; longest_line = $0 }
 END { print longest_line }' file.txt
real    0m5.835s
user    0m5.604s
sys     0m0.204s



$ time sed -rn "/.{$(<file.txt expand -t1 |wc -L)}/{p;q}" file.txt 
real    2m37.348s
user    2m39.990s
sys     0m1.868s

가장 긴 줄을 모두 인쇄하십시오.

$ time perl -e 'while(<>){
           $l=length;
           push @{$k{$l}},$_;
           $m=$l if $l>$m;
         } print @{$k{$m}}' file.txt 
real    0m9.263s
user    0m8.417s
sys     0m0.760s


$ time awk 'length >x { delete y; x=length }
     length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file.txt
real    0m10.220s
user    0m9.925s
sys     0m0.252s


## This is Chris Down's bash solution
$ time ./a.sh < file.txt 
Max line length: 254
Lines matched with that length: 2
real    8m36.975s
user    8m17.495s
sys     0m17.153s

3

가장 긴 줄을 잡으십시오

grep -Em1 "^.{$(wc -L <file.txt)}\$" file.txt 

이 명령은 쉘 구문과 regexp 구문을 혼합하기 때문에 연습 없이는 읽기가 매우 어렵습니다.
설명을 위해 먼저 단순화 된 의사 코드를 사용합니다. 로 시작하는 행 ##은 쉘에서 실행되지 않습니다.
이 단순화 된 코드는 파일 이름 F를 사용하고 가독성을 위해 인용 부호와 정규 표현식의 일부를 생략합니다.

작동 원리

이 명령에는 grep-와 wc호출의 두 부분이 있습니다 .

## grep "^.{$( wc -L F )}$" F

wc프로세스 확장에 사용되는, $( ... )그래서 그것은 전에 실행됩니다 grep. 가장 긴 줄의 길이를 계산합니다. 쉘 확장 구문은 혼란스러운 방식으로 정규식 패턴 구문과 혼합되므로 프로세스 확장을 분해합니다.

## wc -L F
42
## grep "^.{42}$" F

여기서 프로세스 확장은 반환 된 값으로 바뀌어 grep사용되는 명령 줄을 만듭니다 . 이제 정규식을보다 쉽게 ​​읽을 수 있습니다 . 줄의 시작 ( ^)에서 끝 ( $) 까지 정확히 일치합니다 . 그 사이의 표현은 개행을 제외한 모든 문자와 일치하며 42 번 반복됩니다. 즉, 정확히 42 자로 구성된 행입니다.


이제 실제 쉘 명령으로 돌아갑니다. grep옵션 -E( --extended-regexp)을 사용하면 {}가독성을 피할 수 없습니다 . 옵션 -m 1( --max-count=1)은 첫 번째 줄을 찾은 후에 중지합니다. <wc명령을 방지하기 위해, 표준 입력에 파일을 기록 wc길이와 함께 파일 이름을 인쇄에서.

가장 긴 줄은?

두 번 나타나는 파일 이름으로 예제를 더 읽기 쉽게하기 위해 파일 이름에 변수 f를 사용합니다 . $f예제에서 각각 은 파일 이름으로 대체 될 수 있습니다.

f="file.txt"

첫 번째 가장 긴 줄 - 가장 긴 줄 만큼 긴 첫 번째 줄을 표시하십시오 .

grep -E -m1 "^.{$(wc -L <"$f")}\$" "$f"

보기 모두 긴 줄 - 긴 긴 라인 등으로 모든 라인을 :

grep -E "^.{$(wc -L <"$f")}\$" "$f" 

마지막으로 긴 줄 - 긴 줄만큼 긴 마지막 줄을 :

tac "$f" | grep -E -m1 "^.{$(wc -L <"$f")}\$"

하나의 가장 긴 줄 -다른 모든 줄보다 가장 긴 줄을 표시 하거나 실패하십시오.

[ $(grep -E "^.{$(wc -L <"$f")}\$" "$f" | wc -l) = 1 ] && grep -E "^.{$(wc -L <"$f")}\$" "$f" 

(마지막 명령은 완전한 grep 명령을 반복하므로 다른 명령보다 훨씬 비효율적입니다. 출력 wc과 행이 쓴 행 grep이 변수에 저장 되도록 분명히 분해해야 합니다.
가장 긴 행은 실제로 모든 행일 수 있습니다. 변수로 저장하려면 처음 두 줄만 유지하면됩니다.)


와우 큰 대답, 그것으로부터 많은 것을 배웠습니다. 감사합니다
뭔가

2

다음 예제는 dmitry.malikov의 답변에 대한 주석이 되었을 것입니다. 그러나 보이지 않는 주석 공간쓸모없는 사용으로 인해 여기서 볼 수있는 곳을 제시했습니다. ..

이것은 드미트리의 단일 패스 awk 방법 간단한 변형입니다 .
모든 "동일한 가장 긴"줄을 인쇄합니다. (참고. delete arraygawk 확장명입니다).

awk 'length >x { delete y; x=length }
     length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file

1

순수한 배쉬에서 :

#!/bin/bash

_max_length=0
while IFS= read -r _line; do
    _length="${#_line}"
    if (( _length > _max_length )); then
        _max_length=${_length}
        _max_line=( "${_line}" )
    elif (( _length == _max_length )); then
        _max_line+=( "${_line}" )
    fi
done

printf 'Max line length: %d\n' "${_max_length}"
printf 'Lines matched with that length: %d\n' "${#_max_line[@]}"
(( ${#_max_line[@]} )) && printf '%s\n' '----------------' "${_max_line[@]}"

있는 그대로 코드에서 잘못된 결과를 반환 할 수 있습니다. 설정 _max_line[0]=${_line}은 이전에 누적 된 더 짧은 "가장 긴 줄"을 제거하지 않습니다 ... unset _max_line전체 배열을 지 웁니다 ...
Peter.O

@fered 감사합니다. 꽤 빨리 작성되었습니다. 결정된.
Chris Down

0

이를 위해 작은 쉘 스크립트를 개발했습니다. 길이, 줄 번호 및 줄 자체를 80 자와 같은 특정 크기를 초과하는 길이로 표시합니다.

#!/bin/sh

# Author: Surinder

if test $# -lt 2
then
   echo "usage: $0 length file1 file2 ..."
   echo "usage: $0 80 hello.c"
   exit 1
fi

length=$1

shift

LONGLINE=/tmp/longest-line-$$.awk

cat << EOF > $LONGLINE
  BEGIN {
  }

  /.*/ {
    current_length=length(\$0);
    if (current_length >= expected_length) {
       printf("%d at line # %d %s\n", current_length, NR, \$0);
    }
  }

  END {
  }
EOF

for file in $*
do
  echo "$file"
  cat $file | awk -v expected_length=$length -f $LONGLINE |sort -nr
done

rm $LONGLINE

https://github.com/lordofrain/tools/blob/master/longest-line/longest-line.sh


1
몇 가지 개선 사항이 있습니다. 변수를 인용하십시오 . 공백이나 다른 이상한 문자가 포함 된 모든 파일 이름이 손상됩니다. 사용은 $*거의 좋은 생각입니다, 당신이 원하는"$@" . /.*/당신의는 awk그뿐만 아니라 빈 줄과 일치하기 때문에 아무것도하지 않습니다. \$0작은 따옴표를 사용하면 이스케이프를 피할 수 있습니다 'EOF'. 왜 빈 BEGIN{}블록을 사용합니까? 마지막으로, 당신은 cat단지 필요하지 않습니다awk . . . "$file" | . . .
terdon

1
당신은 또한 awk에서 직접 모든 일을 직접 수행해야합니다 :awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
terdon

-3

당신은 사용할 수 있습니다 wc:

wc -L fileName

3
질문을 다시 읽으십시오. 필요한 출력은 가장 긴 줄의 길이가 아니라 가장 긴 줄 자체입니다. 또한 단점 에 관한 Peter.O의 의견을 참조하십시오 wc -L.
manatwork
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.