공백을 포함하여 줄 길이별로 텍스트 파일 정렬


137

다음과 같은 CSV 파일이 있습니다

AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Atlantis, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, Mrs. Plain Example, 1121110 Ternary st. 110 Binary ave .., Atlantis, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, Mr. Plain Example, RI, 일부 도시, 110 Ternary ave., 12345, (999) 123-5555,1.56

공백을 포함하여 줄 길이별로 정렬해야합니다. 다음 명령에는 공백이 포함되어 있지 않습니다. 수정할 수 있도록 수정하는 방법이 있습니까?

cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'

21
저는 Binary Avenue 또는 Ternary Street에 살고 싶습니다. 그 사람들은 확실히 "8192 둥근 숫자입니다"
schnaader

답변:


224

대답

cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-

또는 동일한 길이의 선을 원래 (의도적이지 않은) 하위 분류로 수행하려면 다음을 수행하십시오.

cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-

두 경우 모두, 최종 컷을 위해 awk에서 멀어지면서 언급 된 문제를 해결했습니다.

길이가 일치하는 라인-넥타이의 경우 수행 할 작업 :

이 질문은 길이가 일치하는 줄에 대해 추가 정렬이 필요한지 여부를 지정하지 않았습니다. 나는 이것이 원치 않는 것으로 가정하고 그러한 줄이 서로 정렬되는 것을 방지하고 입력에서 발생하는 상대적 순서로 유지하기 위해 -s( --stable) 사용을 제안했습니다 .

(이 관계를 더 잘 제어하려는 사람들은 sort의 --key옵션을 볼 수 있습니다 .)

질문의 시도 된 솔루션이 실패하는 이유 (awk 라인 재 구축) :

다음의 차이점을 주목하는 것이 흥미 롭습니다.

echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'

그들은 각각 항복

hello   awk   world
hello awk world

의 관련 섹션 (둔한의) 매뉴얼은 단지 당신이 하나 개의 필드를 변경하면 AWK는 (등, 분리 기준) $ 0 전체를 다시 진행하고 있음을 옆으로 언급하고있다. 나는 그것이 미친 행동이 아니라고 생각합니다. 그것은 이것을 가지고 있습니다 :

"마지막으로 필드의 현재 값과 OFS를 사용하여 awk가 전체 레코드를 다시 작성하는 것이 편리한 경우가 있습니다.

 $1 = $1   # force record to be reconstituted
 print $0  # or whatever else with $0

"이로 인해 awk는 레코드를 다시 작성해야합니다."

길이가 같은 일부 라인을 포함한 테스트 입력 :

aa A line   with     MORE    spaces
bb The very longest line in the file
ccb
9   dd equal len.  Orig pos = 1
500 dd equal len.  Orig pos = 2
ccz
cca
ee A line with  some       spaces
1   dd equal len.  Orig pos = 3
ff
5   dd equal len.  Orig pos = 4
g

1
헤 마일입니다. 감사합니다. 가능한 한 OP의 시도한 솔루션의 모양과 일치 시켜서 자신과 나의 중요한 차이점에만 집중할 수 있도록 노력했습니다.
neillb

1
그것도 cat $@망가 졌음을 지적 할 가치가 있습니다. 당신은 절대적으로 그것을 인용하고 싶습니다cat "$@"
tripleee

27

neillbAWK 솔루션은 실제로 사용 awk하고 싶을 때 유용 하며, 그 이유가 무엇인지 설명하지만 원하는 작업을 신속하게 수행하고 수행하는 작업을 신경 쓰지 않는 경우 하나의 솔루션을 사용하는 것입니다 sort()입력 라인을 반복하는 커스텀 caparison 루틴을 가진 Perl의 기능. 하나의 라이너는 다음과 같습니다.

perl -e 'print sort { length($a) <=> length($b) } <>'

STDIN을 수신 cat하거나 ( 쉘 리다이렉션을 통해) 파이프 라인에 필요 하거나 파일 이름을 다른 인수로 perl하여 파일을 열 수 있습니다.

내가 스왑 그래서 내 경우에는 내가 먼저 긴 줄을 필요로 $a하고 $b비교에.


입력 파일에 숫자 및 영숫자 행이 포함 된 경우 awk가 예기치 않은 정렬을 유발하기 때문에이 방법이 더 좋습니다. oneline 명령 : $ cat testfile | perl -e 'print sort {length ($ a) <=> length ($ b)} <>'
alemol

빠른! 출력이 다른 파일로 경로 재 지정 될 때 46 초 라인 파일 (한 줄에 한 단어)이 1 초 cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
미만이었습니다

StrawberryPerl이 설치된 Windows :type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
bryc

14

대신이 명령을 시도하십시오.

awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-

10

벤치 마크 결과

다음은이 질문에 대한 다른 답변의 솔루션에 대한 벤치 마크 결과입니다.

테스트 방식

  • 빠른 기계에서 평균 10 회 연속 실행
  • 펄 5.24
  • awk 3.1.5 (gawk 4.1.0 배 ~ 2 % 빠름)
  • 입력 파일은 550MB, 6 백만 줄의 괴물입니다 (British National Corpus txt)

결과

  1. 칼렙 perl 솔루션 은 11.2 초 걸렸습니다
  2. 나의 perl 솔루션 은 11.6 초 걸렸다
  3. neillb의 awk 솔루션 # 1은 20 초가 걸렸습니다
  4. neillb의 awk 솔루션 # 2는 23 초가 걸렸습니다
  5. 아누 바 awk 솔루션 은 24 초가 걸렸습니다
  6. 조나단 awk 솔루션 은 25 초가 걸렸습니다
  7. Fretz의 bash솔루션 은 보다 400 배 더 오래 걸립니다 awk(100000 줄의 잘린 테스트 사례 사용). 잘 작동하고 영원히 걸립니다.

추가 perl옵션

또한 다른 Perl 솔루션을 추가했습니다.

perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

6

순수한 배쉬 :

declare -a sorted

while read line; do
  if [ -z "${sorted[${#line}]}" ] ; then          # does line length already exist?
    sorted[${#line}]="$line"                      # element for new length
  else
    sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length
  fi
done < data.csv

for key in ${!sorted[*]}; do                      # iterate over existing indices
  echo -e "${sorted[$key]}"                       # echo lines with equal length
done

3

length()함수는 공간을 포함한다. 파이프 라인을 약간만 조정하면됩니다 ( UUOC 피하기 포함 ).

awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'

sed명령 은 명령에 의해 추가 된 숫자와 콜론을 직접 제거합니다 awk. 또는 형식을 awk다음 에서 유지하십시오 .

awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'

2

파일에 숫자로 시작하는 줄이 포함되어 있으면이 솔루션이 작동하지 않는다는 것을 알았습니다. 숫자로 정렬 된 모든 줄과 숫자로 정렬되기 때문입니다. 이 솔루션은 제공하는 것입니다 대신 플래그 (일반 숫자-종류) (숫자-종류) :sort-g-n

awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-

2
안녕, 마커스 길이가 일치하는 줄의 경우를 제외하고는 줄 길이와 달리 줄 내용 (숫자 여부)을 정렬에 영향을 미치지 않습니다. 이것이 당신이 의미 한 것입니까? 그러한 경우, 개선 정렬 -n을 위해 제안 된 정렬 방법을 찾지 -g못했습니다. 나는 이제 내 대답에서 등 길이 줄의 하위 분류를 금지하는 방법 (을 사용하여 --stable)을 다루었습니다 . 그것이 당신이 의도 한 것이 든 아니든, 내 관심을 가져 주셔서 감사합니다! 또한 테스트 할 입력을 추가했습니다.
neillb

4
아니요, 분류하여 설명하겠습니다. awk부품 만으로 선 길이와 공백이 앞에 붙은 선 목록을 생성합니다. 배관 sort -n은 예상대로 작동합니다. 그러나 해당 줄 중 처음에 이미 숫자가있는 경우 해당 줄은 길이 + 공백 + 숫자로 시작합니다. sort -n해당 공간을 무시하고 길이 + 숫자에서 연결된 하나의 숫자로 처리합니다. 은 Using -g플래그 대신 정확한 종류를 산출, 최초의 우주에서 중지됩니다. 접두사가 붙은 파일이있는 파일을 만들어서 단계별로 명령을 실행하여 직접 해보십시오.
Markus Amalthea Magnuson

1
또한 sort -n공간 을 무시하고 잘못된 정렬을 생성 한다는 것을 알았습니다 . sort -g올바른 순서를 출력합니다.
Robert Smith

나는 함께 설명 된 문제를 재현 할 수 없습니다 -n에서 sort (GNU coreutils) 8.21. 이 info설명서에는 -g효율성이 낮고 정확하지 않은 것으로 설명되어 있으므로 숫자를 부동 소수점으로 변환하므로 필요하지 않은 경우에는 사용하지 마십시오.
phils

nb 설명서 -n : "숫자 정렬. 숫자는 각 줄을 시작하고 선택적인 공백, 선택적인 '-'부호 및 천 단위 구분 기호로 분리 될 수있는 0 개 이상의 숫자, 선택적으로 소수점 문자 및 0 개 이상의 숫자로 구성됩니다. 빈 숫자는 '0'으로 취급됩니다. 'LC_NUMERIC'로케일은 소수점 문자와 천 단위 구분 기호를 지정합니다. 기본적으로 공백은 공백 또는 탭이지만 'LC_CTYPE'로케일은이를 변경할 수 있습니다. "
phils


2

1) 순수한 awk 솔루션. 줄 길이가 1024보다 클 수 없다고 가정 해 봅시다.

고양이 파일 이름 | awk 'BEGIN {분 = 1024; s = "";} {l = 길이 ($ 0); if (l <min) {min = l; s = $ 0;}} END {print s} '

2) 모든 라인에 단 하나의 단어가 있다고 가정하지만 모든 라인에 동일한 수의 단어가있는 경우 모든 재 작업이 가능합니다.

LINES = $ (고양이 파일명); $ LINES의 k printf "$ k"를하십시오; 에코 $ k | 화장실 -L; 완료 | 정렬 -k2 | 헤드 -n 1 | 컷 -d ""-f1


1

길이별로 행을 정렬하는 멀티 바이트 호환 방법이 있습니다. 필요합니다 :

  1. wc -m 사용 가능합니다 (macOS 있음).
  2. 현재 로케일은 멀티 바이트 문자를 지원합니다 (예 : 설정). LC_ALL=UTF-8 . .bash_profile에서 또는 다음 명령 앞에 추가하여 간단히 설정할 수 있습니다.
  3. testfile 로케일과 일치하는 문자 인코딩이 있습니다 (예 : UTF-8).

전체 명령은 다음과 같습니다.

cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-

부분적으로 설명하기 :

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l);← awk 변수에 각 줄의 사본을 만들고 l매번 이중 이스케이프 '하므로 줄을 쉘 명령으로 안전하게 에코 할 수 있습니다 ( \0478 진수 표기법의 작은 따옴표입니다).
  • cmd=sprintf("echo \047%s\047 | wc -m", l);← 이것은 우리가 실행할 명령이며, 이스케이프 된 줄을에 에코합니다 wc -m.
  • cmd | getline c;← 명령을 실행하고 awk 변수에 반환 된 문자 수 값을 복사합니다 c.
  • close(cmd); ← 한 프로세스에서 열린 파일 수에 대한 시스템 제한을 피하기 위해 쉘 명령에 파이프를 닫으십시오.
  • sub(/ */, "", c);←는에서 반환 한 문자 수 값에서 공백을 제거합니다 wc.
  • { print c, $0 } ← 줄의 문자 수 값, 공백 및 원래 줄을 인쇄합니다.
  • | sort -ns← 줄을 앞에 붙인 문자 수 값 -n으로 정렬하고 ( ) 안정적인 정렬 순서를 유지합니다 ( -s).
  • | cut -d" " -f2- ← 앞에 붙은 문자 수 값을 제거합니다.

각 줄에 대해 하위 명령을 실행해야하므로 속도가 느립니다 (빠른 Macbook Pro에서는 초당 160 줄).

또는 gawk(버전 3.1.5부터 gawk는 멀티 바이트를 인식) 단독으로 수행하면 훨씬 빠릅니다. awk에서 쉘 명령을 통해 행을 안전하게 전달하기 위해 모든 이스케이프 및 이중 따옴표를 작성하는 데 많은 문제가 있지만 추가 소프트웨어를 설치할 필요가없는 유일한 방법입니다 (기본적으로 gawk는 사용할 수 없습니다) 맥 OS).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.