ฉันจะเรียงลำดับรายการที่คั่นด้วยบรรทัดเดียวได้อย่างไร


11

ฉันมีจำนวนบรรทัด (หรือหลายบรรทัด) ที่คั่นด้วยอักขระที่กำหนดเอง เครื่องมือ UNIX ใดที่ฉันสามารถใช้เพื่อเรียงลำดับรายการของแต่ละบรรทัดเป็นตัวเลขรักษาตัวคั่น

ตัวอย่างรวมถึง:

  • รายการของตัวเลข อินพุต: 10 50 23 42; เรียง:10 23 42 50
  • ที่อยู่ IP; อินพุต: 10.1.200.42; เรียง:1.10.42.200
  • CSV; อินพุต: 1,100,330,42; เรียง:1,42,100,330
  • ท่อที่คั่น; อินพุต: 400|500|404; เรียง:400|404|500

เนื่องจากตัวคั่นมีความอิสระให้คุณตอบ (หรือขยาย) คำตอบโดยใช้ตัวคั่นตัวเดียวที่คุณเลือก


8
คุณควรจะโพสต์ไปบน codegolf :)
ivanivan

1
มีคำถามที่คล้ายกันที่นี่ฉันต้องการจะเพิ่มลิงค์ของมันตัวอักษรคำภายในชื่อไฟล์โดยใช้การเรียงลำดับ?
αғsнιη

เพียงคำใบ้ที่cutรองรับตัวคั่นโดยพลการพร้อม-dตัวเลือก
Oleg Lobachev

โปรดอธิบายว่าตัวอย่างของDSVทั้งสี่นั้นอยู่ในไฟล์เดียวกันหรือเป็นตัวอย่างจากไฟล์ที่แตกต่างกันสี่ไฟล์
agc

2
เห็นบางส่วนของความคิดเห็นอื่น ๆ : ตัวคั่นเป็นโดยพลการ แต่จะใช้อย่างต่อเนื่องในการป้อนข้อมูล สมมติความฉลาดในส่วนของผู้ผลิตข้อมูลว่าพวกเขาจะไม่ใช้เครื่องหมายจุลภาคเป็นตัวคั่นและในข้อมูล (ตัวอย่างเช่น4,325 comma 55 comma 42,430จะไม่เกิดขึ้นหรือ1.5 period 4.2)
Jeff Schaller

คำตอบ:


12

คุณสามารถบรรลุสิ่งนี้ด้วย:

tr '.' '\n' <<<"$aline" | sort -n | paste -sd'.' -

แทนที่จุด .ด้วยตัวคั่นของคุณ
เพิ่ม-uไปที่sortคำสั่งดังกล่าวจะลบที่ซ้ำกัน


หรือด้วยgawk( GNU awk ) เราสามารถประมวลผลหลายบรรทัดในขณะที่ข้างต้นสามารถขยายได้ด้วย:

gawk -v SEP='*' '{ i=0; split($0, arr, SEP); 
    while ( ++i<=asort(arr) ){ printf("%s%s", i>1?SEP:"", arr[i]) }; 
        print "" 
}' infile

เปลี่ยน*เป็นตัวคั่นฟิลด์ในSEP='*'กับตัวคั่น


หมายเหตุ:
คุณอาจต้องใช้-g, --general-numeric-sortตัวเลือกsortแทน-n, --numeric-sortเพื่อจัดการกับคลาสของตัวเลขใด ๆ (จำนวนเต็ม, ลอย, วิทยาศาสตร์, เลขฐานสิบหก, ฯลฯ )

$ aline='2e-18,6.01e-17,1.4,-4,0xB000,0xB001,23,-3.e+11'
$ tr ',' '\n' <<<"$aline" |sort -g | paste -sd',' -
-3.e+11,-4,2e-18,6.01e-17,1.4,23,0xB000,0xB001

ในawkการเปลี่ยนแปลงความต้องการไม่ว่าจะยังคงจัดการเหล่านั้น


10

ใช้perlมีรุ่นที่ชัดเจน; แยกข้อมูลจัดเรียงเข้าร่วมสำรองอีกครั้ง

ตัวคั่นต้องถูกแสดงรายการสองครั้ง (หนึ่งครั้งในsplitและอีกครั้งในjoin)

เช่นสำหรับ ,

perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'

ดังนั้น

echo 1,100,330,42 | perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
1,42,100,330

เนื่องจากsplitเป็น regex อักขระอาจต้องมีการอ้างอิง:

echo 10.1.200.42 | perl -lpi -e '$_=join(".",sort {$a <=> $b} split(/\./))'
1.10.42.200

โดยใช้-aและ-Fตัวเลือกคุณสามารถลบแยกได้ ด้วยการ-pวนซ้ำเมื่อก่อนและตั้งค่าผลลัพธ์เป็น$_ซึ่งจะพิมพ์โดยอัตโนมัติ:

perl -F'/\./' -aple '$_=join(".", sort {$a <=> $b} @F)'

4
คุณสามารถใช้ตัวเลือกแทนการใช้-l chompที่เพิ่มขึ้นบรรทัดใหม่เมื่อพิมพ์ ดูเพิ่มเติม-a(พร้อม-F) สำหรับส่วนที่แยก
Stéphane Chazelas

1
ด้วย-lและ-Fก็จะยิ่งดีกว่า:perl -F'/\./' -le 'print join(".", sort {$a <=> $b} @F)'
Muru

@ StéphaneChazelasขอบคุณสำหรับ-lตัวเลือก; ฉันจะพลาดมัน!
สตีเฟ่นแฮร์ริส

1
@muru ฉันไม่ได้ใช้การ-Fตั้งค่าเริ่มต้นเพราะมันไม่ทำงานอย่างถูกต้องในทุกรุ่น (เช่นบรรทัดของคุณใน CentOS 7 - perl 5.16.3 - ส่งกลับค่าว่างเปล่าแม้ว่ามันจะทำงานได้ดีใน Debian 9) แต่เมื่อรวมกับ-pมันจะให้ผลลัพธ์ที่เล็กกว่าเล็กน้อยดังนั้นฉันจึงเพิ่มมันเข้าไปแทนคำตอบ แสดงวิธีการ-Fใช้ ขอบคุณ!
สตีเฟ่นแฮร์ริส

2
@StephenHarris นั่นเป็นเพราะ perl รุ่นใหม่ที่เพิ่มขึ้นโดยอัตโนมัติ-aและ-nตัวเลือกเมื่อ-Fมีการใช้และ-nเมื่อ-aมีการใช้ ... ดังนั้นเพียงแค่เปลี่ยน-leเป็น-lane
Sundeep

4

การใช้ Python และแนวคิดที่คล้ายกันในคำตอบของ Stephen Harris :

python3 -c 'import sys; c = sys.argv[1]; sys.stdout.writelines(map(lambda x: c.join(sorted(x.strip().split(c), key=int)) + "\n", sys.stdin))' <delmiter>

ดังนั้นสิ่งที่ชอบ:

$ cat foo
10.129.3.4
1.1.1.1
4.3.2.1
$ python3 -c 'import sys; c = sys.argv[1]; sys.stdout.writelines(map(lambda x: c.join(sorted(x.strip().split(c), key=int)) + "\n", sys.stdin))' . < foo
3.4.10.129
1.1.1.1
1.2.3.4

น่าเสียดายที่ต้องทำ I / O ด้วยตนเองทำให้สิ่งนี้ดูหรูหราน้อยกว่ารุ่น Perl


3

สคริปต์ทุบตี:

#!/usr/bin/env bash

join_by(){ local IFS="$1"; shift; echo "$*"; }

IFS="$1" read -r -a tokens_array <<< "$2"
IFS=$'\n' sorted=($(sort -n <<<"${tokens_array[*]}"))
join_by "$1" "${sorted[@]}"

ตัวอย่าง:

$ ./sort_delimited_string.sh "." "192.168.0.1"
0.1.168.192

ขึ้นอยู่กับ


3

เปลือก

การโหลดภาษาระดับสูงกว่าต้องใช้เวลา
สำหรับสองสามบรรทัดเชลล์เองอาจเป็นโซลูชัน
เราสามารถใช้คำสั่งภายนอกและของคำสั่งsort trหนึ่งมีประสิทธิภาพค่อนข้างในการเรียงลำดับบรรทัดและอื่น ๆ มีประสิทธิภาพในการแปลงหนึ่งตัวคั่นเป็นบรรทัดใหม่:

#!/bin/bash
shsort(){
           while IFS='' read -r line; do
               echo "$line" | tr "$1" '\n' |
               sort -n   | paste -sd "$1" -
           done <<<"$2"
    }

shsort ' '    '10 50 23 42'
shsort '.'    '10.1.200.42'
shsort ','    '1,100,330,42'
shsort '|'    '400|500|404'
shsort ','    '3 b,2       x,45    f,*,8jk'
shsort '.'    '10.128.33.6
128.17.71.3
44.32.63.1'

ต้องทุบตีเพราะการใช้<<<เท่านั้น หากถูกแทนที่ด้วย here-doc โซลูชันจะใช้ได้สำหรับ posix
นี้สามารถที่จะจัดเรียงเขตข้อมูลที่มีแท็บช่องว่างหรืออักขระเปลือก glob ( *, ?, [) ไม่ขึ้นบรรทัดใหม่เนื่องจากแต่ละบรรทัดกำลังเรียงลำดับ

เปลี่ยน<<<"$2"เป็น<"$2"ประมวลผลชื่อไฟล์และเรียกว่า:

shsort '.'    infile

ตัวคั่นจะเหมือนกันสำหรับไฟล์ทั้งหมด หากนั่นเป็นข้อ จำกัด ก็สามารถปรับปรุงได้

อย่างไรก็ตามไฟล์ที่มีเพียง 6,000 บรรทัดจะใช้เวลาประมวลผล 15 วินาที แท้จริงแล้วเชลล์ไม่ใช่เครื่องมือที่ดีที่สุดในการประมวลผลไฟล์

awk

สำหรับมากกว่าสองสามบรรทัด (มากกว่าสิบสองสาม) มันจะดีกว่าที่จะใช้ภาษาการเขียนโปรแกรมจริง โซลูชัน awk อาจเป็น:

#!/bin/bash
awksort(){
           gawk -v del="$1" '{
               split($0, fields, del)
               l=asort(fields)
               for(i=1;i<=l;i++){
                   printf( "%s%s" , (i==0)?"":del , fields[i] )
               }
               printf "\n"
           }' <"$2"
         }

awksort '.'    infile

ซึ่งใช้เวลาเพียง 0.2 วินาทีสำหรับไฟล์ 6,000 บรรทัดเดียวกันที่กล่าวถึงข้างต้น

เข้าใจว่า<"$2"สามารถเปลี่ยนไฟล์<<<"$2"for เป็นบรรทัดภายในตัวแปร shell ได้

Perl

ทางออกที่เร็วที่สุดคือ Perl

#!/bin/bash
perlsort(){  perl -lp -e '$_=join("'"$1"'",sort {$a <=> $b} split(/['"$1"']/))' <<<"$2";   }

perlsort ' '    '10 50 23 42'
perlsort '.'    '10.1.200.42'
perlsort ','    '1,100,330,42'
perlsort '|'    '400|500|404'
perlsort ','    '3 b,2       x,45    f,*,8jk'
perlsort '.'    '10.128.33.6
128.17.71.3
44.32.63.1'

หากคุณต้องการเรียงลำดับการเปลี่ยนแปลงไฟล์<<<"$a"เป็นอย่างง่าย"$a"และเพิ่ม-iไปยังตัวเลือก perl เพื่อสร้างรุ่นไฟล์ "แทนที่":

#!/bin/bash
perlsort(){  perl -lpi -e '$_=join("'"$1"'",sort {$a <=> $b} split(/['"$1"']/))' "$2"; }

perlsort '.' infile; exit

2

ใช้sedเพื่อเรียงลำดับ octet ของที่อยู่ IP

sedไม่ได้มีในตัวsortฟังก์ชั่น แต่ถ้าข้อมูลของคุณเป็นข้อ จำกัด อย่างเพียงพอในช่วง (เช่นมีที่อยู่ IP) คุณสามารถสร้างสคริปต์ sed ว่าการดำเนินการด้วยตนเองง่ายๆเรียงฟอง กลไกพื้นฐานคือการค้นหาตัวเลขที่อยู่ติดกันซึ่งไม่เรียบร้อย หากตัวเลขไม่ถูกต้องให้สลับ

sedสคริปต์ตัวเองมีสองคำสั่งค้นหาและแลกเปลี่ยนสำหรับคู่ของตัวเลขออกจากการสั่งซื้อแต่ละหนึ่งสำหรับสองคู่แรกของ octets (บังคับตัวคั่นท้ายที่จะนำเสนอในการทำเครื่องหมายจุดสิ้นสุดของ octet ที่สาม) และ ที่สองสำหรับ octet คู่ที่สาม (ลงท้ายด้วย EOL) หากมีการสลับเกิดขึ้นโปรแกรมจะไปที่ด้านบนสุดของสคริปต์เพื่อค้นหาหมายเลขที่ไม่เรียบร้อย มิฉะนั้นจะออก

สคริปต์ที่สร้างขึ้นเป็นส่วนหนึ่ง:

$ head -n 3 generated.sed
:top
s/255\.254\./254.255./g; s/255\.254$/254.255/
s/255\.253\./253.255./g; s/255\.253$/253.255/

# ... middle of the script omitted ...

$ tail -n 4 generated.sed
s/2\.1\./1.2./g; s/2\.1$/1.2/
s/2\.0\./0.2./g; s/2\.0$/0.2/
s/1\.0\./0.1./g; s/1\.0$/0.1/
ttop

วิธีการนี้จะกำหนดช่วงเวลาเป็นตัวคั่นอย่างหนักซึ่งจะต้องมีการหลบหนีมิฉะนั้นจะเป็น "พิเศษ" ในไวยากรณ์นิพจน์ทั่วไป (อนุญาตให้ใช้อักขระใด ๆ )

ในการสร้างสคริปต์แบบวนซ้ำลูปนี้จะทำ:

#!/bin/bash

echo ':top'

for (( n = 255; n >= 0; n-- )); do
  for (( m = n - 1; m >= 0; m-- )); do
    printf '%s; %s\n' "s/$n\\.$m\\./$m.$n./g" "s/$n\\.$m\$/$m.$n/"
  done
done

echo 'ttop'

sort-ips.sedเปลี่ยนเส้นทางการส่งออกของสคริปต์ที่ไปยังแฟ้มอื่นพูด

การรันตัวอย่างสามารถมีลักษณะดังนี้:

ip=$((RANDOM % 256)).$((RANDOM % 256)).$((RANDOM % 256)).$((RANDOM % 256))
printf '%s\n' "$ip" | sed -f sort-ips.sed

รูปแบบต่อไปนี้ในสคริปต์การสร้างใช้เครื่องหมายคำขอบเขต\<และ\>เพื่อกำจัดความต้องการของการทดแทนที่สอง สิ่งนี้จะลดขนาดของสคริปต์ที่สร้างขึ้นจาก 1.3 MB เหลือเพียง 900 KB และลดเวลาการทำงานของsedตัวเองลงอย่างมาก(เหลือประมาณ 50% -75% ของต้นฉบับขึ้นอยู่กับsedการใช้งาน)

#!/bin/bash

echo ':top'

for (( n = 255; n >= 0; --n )); do
  for (( m = n - 1; m >= 0; --m )); do
      printf '%s\n' "s/\\<$n\\>\\.\\<$m\\>/$m.$n/g"
  done
done

echo 'ttop'

1
ความคิดที่น่าสนใจ แต่ดูเหมือนจะซับซ้อนเกินความจริงเล็กน้อย
Matt

1
@ Matt มันเป็นประเด็น การเรียงลำดับอะไรก็ตามsedนั้นไร้สาระซึ่งเป็นเหตุผลว่าทำไมมันถึงเป็นความท้าทายที่น่าสนใจ
Kusalananda

2

ที่นี่ทุบตีบางอย่างที่คาดเดาตัวคั่นด้วยตัวเอง:

#!/bin/bash

delimiter="${1//[[:digit:]]/}"
if echo $delimiter | grep -q "^\(.\)\1\+$"
then
  delimiter="${delimiter:0:1}"
  if [[ -z $(echo $1 | grep "^\([0-9]\+"$delimiter"\([0-9]\+\)*\)\+$") ]]
  then
    echo "You seem to have empty fields between the delimiters."
    exit 1
  fi
  if [[ './\' == *$delimiter* ]]
  then
    n=$( echo $1 | sed "s/\\"$delimiter"/\\n/g" | sort -n | tr '\n' ' ' | sed -e "s/\\s/\\"$delimiter"/g")
  else
    n=$( echo $1 | sed "s/"$delimiter"/\\n/g" | sort -n | tr '\n' ' ' | sed -e "s/\\s/"$delimiter"/g")
  fi
  echo ${n%$delimiter}
  exit 0
else
  echo "The string does not consist of digits separated by one unique delimiter."
  exit 1
fi

อาจไม่มีประสิทธิภาพหรือไม่สะอาด แต่ใช้งานได้

bash my_script.sh "00/00/18/29838/2"การใช้งานเช่น

ส่งคืนข้อผิดพลาดเมื่อตัวคั่นเดียวกันไม่ถูกใช้อย่างสม่ำเสมอหรือเมื่อตัวคั่นสองตัวขึ้นไปติดตามกัน

หากตัวคั่นที่ใช้เป็นอักขระพิเศษแสดงว่าตัวนั้นถูกหลบหนี (มิฉะนั้นsedจะส่งคืนข้อผิดพลาด)


เป็นแรงบันดาลใจนี้
agc

2

คำตอบนี้อยู่บนพื้นฐานของความเข้าใจผิดของ Q. แต่ในบางกรณีมันเกิดขึ้นเพื่อแก้ไขให้ถูกต้องอยู่แล้ว หากอินพุตเป็นตัวเลขที่เป็นธรรมชาติทั้งหมดและมีตัวคั่นเพียงหนึ่งตัวต่อบรรทัด (เช่นเดียวกับข้อมูลตัวอย่างใน Q) มันจะทำงานได้อย่างถูกต้อง นอกจากนี้ยังจะจัดการไฟล์ที่มีเส้นที่แต่ละคนมีตัวคั่นของตัวเองซึ่งเป็นมากกว่าสิ่งที่ถูกถาม

ฟังก์ชั่นเชลล์นี้readจากอินพุตมาตรฐานใช้การแทนที่พารามิเตอร์ POSIXเพื่อค้นหาตัวคั่นเฉพาะในแต่ละบรรทัด (เก็บไว้ใน$d) และใช้trเพื่อแทนที่$dด้วย newline \nและsortข้อมูลของบรรทัดนั้นแล้วเรียกคืนตัวคั่นดั้งเดิมของแต่ละบรรทัด:

sdn() { while read x; do
            d="${x#${x%%[^0-9]*}}"   d="${d%%[0-9]*}"
            x=$(echo -n "$x" | tr "$d" '\n' | sort -g | tr '\n' "$d")
            echo ${x%?}
        done ; }

นำไปใช้กับข้อมูลที่ให้ไว้ในOP :

printf "%s\n" "10 50 23 42" "10.1.200.42" "1,100,330,42" "400|500|404" | sdn

เอาท์พุท:

10 23 42 50
1.10.42.200
1,42,100,330
400|404|500

ตัวคั่นในบรรทัดใด ๆ จะสอดคล้องกัน โซลูชันทั่วไปที่อนุญาตให้ผู้ใช้ประกาศตัวคั่นนั้นยอดเยี่ยม แต่คำตอบอาจถือว่าตัวคั่นใด ๆ ที่มีความหมายต่อพวกเขา
Jeff Schaller

2

สำหรับตัวคั่นโดยพลการ:

perl -lne '
  @list = /\D+|\d+/g;
  @sorted = sort {$a <=> $b} grep /\d/, @list;
  for (@list) {$_ = shift@sorted if /\d/};
  print @list'

ในการป้อนข้อมูลเช่น:

5,4,2,3
6|5,2|4
There are 10 numbers in those 3 lines

มันให้:

2,3,4,5
2|4,5|6
There are 3 numbers in those 10 lines

0

สิ่งนี้ควรจัดการตัวคั่นที่ไม่ใช่ตัวเลข (0-9) ตัวอย่าง:

x='1!4!3!5!2'; delim=$(echo "$x" | tr -d 0-9 | cut -b1); echo "$x" | tr "$delim" '\n' | sort -g | tr '\n' "$delim" | sed "s/$delim$/\n/"

เอาท์พุท:

1!2!3!4!5

0

ด้วยperl:

$ # -a to auto-split on whitespace, results in @F array
$ echo 'foo baz v22 aimed' | perl -lane 'print join " ", sort @F'
aimed baz foo v22
$ # {$a <=> $b} for numeric comparison, {$b <=> $a} will give descending order
$ echo '1,100,330,42' | perl -F, -lane 'print join ",", sort {$a <=> $b} @F'
1,42,100,330

ด้วยrubyซึ่งค่อนข้างคล้ายกับperl

$ # -a to auto-split on whitespace, results in $F array
$ # $F is sorted and then joined using the given string
$ echo 'foo baz v22 aimed' | ruby -lane 'print $F.sort * " "'
aimed baz foo v22

$ # (&:to_i) to convert string to integer
$ echo '1,100,330,42' | ruby -F, -lane 'print $F.sort_by(&:to_i) * ","'
1,42,100,330

$ echo '10.1.200.42' | ruby -F'\.' -lane 'print $F.sort_by(&:to_i) * "."'
1.10.42.200


คำสั่งที่กำหนดเองและส่งผ่านเพียงสตริงตัวคั่น (ไม่ใช่ regex) จะทำงานหากอินพุตมีข้อมูลลอยตัวเช่นกัน

$ # by default join uses value of $,
$ sort_line(){ ruby -lne '$,=ENV["d"]; print $_.split($,).sort_by(&:to_f).join' ; }

$ s='103,14.5,30,24'
$ echo "$s" | d=',' sort_line
14.5,24,30,103
$ s='10.1.200.42'
$ echo "$s" | d='.' sort_line
1.10.42.200

$ # for file input
$ echo '123--87--23' > ip.txt
$ echo '3--12--435--8' >> ip.txt
$ d='--' sort_line <ip.txt
23--87--123
3--8--12--435


คำสั่งที่กำหนดเองสำหรับ perl

$ sort_line(){ perl -lne '$d=$ENV{d}; print join $d, sort {$a <=> $b} split /\Q$d/' ; }
$ s='123^[]$87^[]$23'
$ echo "$s" | d='^[]$' sort_line 
23^[]$87^[]$123


อ่านเพิ่มเติม - ฉันมีรายการของ perl / ruby ​​one-liners ที่มีประโยชน์อยู่แล้ว


0

ต่อไปนี้เป็นรูปแบบของคำตอบของ Jeffในแง่ที่ว่ามันสร้างsedสคริปต์ที่จะทำการเรียงลำดับของ Bubble แต่แตกต่างกันมากพอที่จะรับประกันคำตอบของตัวเอง

ความแตกต่างคือแทนที่จะสร้างนิพจน์ปกติพื้นฐาน O (n ^ 2) สิ่งนี้จะสร้าง O (n) นิพจน์ปกติที่ขยายเพิ่ม สคริปต์ที่ได้จะมีขนาดใหญ่ประมาณ 15 KB เวลาทำงานของsedสคริปต์อยู่ในเสี้ยววินาที (ใช้เวลานานขึ้นเล็กน้อยในการสร้างสคริปต์)

มัน จำกัด การเรียงลำดับจำนวนเต็มบวกที่คั่นด้วยจุด แต่ไม่ จำกัด ขนาดของจำนวนเต็ม (เพิ่ม255ในวงวนหลัก) หรือจำนวนเต็ม ตัวคั่นอาจเปลี่ยนแปลงได้โดยการเปลี่ยนdelim='.'รหัส

หัวของฉันทำเสร็จแล้วเพื่อรับการแสดงออกปกติดังนั้นฉันจะออกจากการอธิบายรายละเอียดสำหรับวันอื่น

#!/bin/bash

# This function creates a extended regular expression
# that matches a positive number less than the given parameter.
lt_pattern() {
    local n="$1"  # Our number.
    local -a res  # Our result, an array of regular expressions that we
                  # later join into a string.

    for (( i = 1; i < ${#n}; ++i )); do
        d=$(( ${n: -i:1} - 1 )) # The i:th digit of the number, from right to left, minus one.

        if (( d >= 0 )); then
            res+=( "$( printf '%d[0-%d][0-9]{%d}' "${n:0:-i}" "$d" "$(( i - 1 ))" )" )
        fi
    done

    d=${n:0:1} # The first digit of the number.
    if (( d > 1 )); then
        res+=( "$( printf '[1-%d][0-9]{%d}' "$(( d - 1 ))" "$(( ${#n} - 1 ))" )" )
    fi

    if (( n > 9 )); then
        # The number is 10 or larger.
        res+=( "$( printf '[0-9]{1,%d}' "$(( ${#n} - 1 ))" )" )
    fi

    if (( n == 1 )); then
        # The number is 1. The only thing smaller is zero.
        res+=( 0 )
    fi

    # Join our res array of expressions into a '|'-delimited string.
    ( IFS='|'; printf '%s\n' "${res[*]}" )
}

echo ':top'

delim='.'

for (( n = 255; n > 0; --n )); do
    printf 's/\\<%d\\>\\%s\\<(%s)\\>/\\1%s%d/g\n' \
        "$n" "$delim" "$( lt_pattern "$n" )" "$delim" "$n"
done

echo 'ttop'

สคริปต์จะมีลักษณะดังนี้:

$ bash generator.sh >script.sed
$ head -n 5 script.sed
:top
s/\<255\>\.\<(25[0-4][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.255/g
s/\<254\>\.\<(25[0-3][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.254/g
s/\<253\>\.\<(25[0-2][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.253/g
s/\<252\>\.\<(25[0-1][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.252/g
$ tail -n 5 script.sed
s/\<4\>\.\<([1-3][0-9]{0})\>/\1.4/g
s/\<3\>\.\<([1-2][0-9]{0})\>/\1.3/g
s/\<2\>\.\<([1-1][0-9]{0})\>/\1.2/g
s/\<1\>\.\<(0)\>/\1.1/g
ttop

แนวคิดเบื้องหลังนิพจน์ทั่วไปที่สร้างขึ้นคือการจับคู่รูปแบบสำหรับตัวเลขที่น้อยกว่าจำนวนเต็มแต่ละค่า ตัวเลขทั้งสองนี้จะไม่เป็นไปตามลำดับและสลับกัน นิพจน์ทั่วไปจะถูกจัดกลุ่มเป็นตัวเลือกหรือตัวเลือกต่างๆ ให้ความสนใจอย่างใกล้ชิดกับช่วงที่ผนวกเข้ากับแต่ละรายการซึ่งบางครั้งก็{0}หมายถึงรายการที่อยู่ก่อนหน้านั้นจะถูกตัดออกจากการค้นหา ตัวเลือก regex จากซ้ายไปขวาจับคู่หมายเลขที่เล็กกว่าหมายเลขที่กำหนดโดย:

  • สถานที่ที่
  • สถานที่นับสิบ
  • สถานที่หลายร้อย
  • (ต่อไปตามต้องการสำหรับจำนวนที่มากขึ้น)
  • หรือมีขนาดเล็กกว่า (จำนวนหลัก)

หากต้องการสะกดตัวอย่างใช้101(พร้อมช่องว่างเพิ่มเติมเพื่อให้สามารถอ่านได้):

s/ \<101\> \. \<(10[0-0][0-9]{0} | [0-9]{1,2})\> / \1.101 /g

ที่นี่การสลับครั้งแรกอนุญาตให้ตัวเลข 100 ถึง 100; การสลับที่สองอนุญาตให้ 0 ถึง 99

ตัวอย่างอื่นคือ154:

s/ \<154\> \. \<(15[0-3][0-9]{0} | 1[0-4][0-9]{1} | [0-9]{1,2})\> / \1.154 /g

ตัวเลือกแรกคือ 150 ถึง 153 วินาทีอนุญาตให้ 100 ถึง 149 และสุดท้ายอนุญาตให้ 0 ถึง 99

ทดสอบสี่ครั้งในลูป:

for test_run in {1..4}; do
    nums=$(( RANDOM%256 )).$(( RANDOM%256 )).$(( RANDOM%256 )).$(( RANDOM%256 ))
    printf 'nums=%s\n' "$nums"
    sed -E -f script.sed <<<"$nums"
done

เอาท์พุท:

nums=90.19.146.232
19.90.146.232
nums=8.226.70.154
8.70.154.226
nums=1.64.96.143
1.64.96.143
nums=67.6.203.56
6.56.67.203

-2

แยกอินพุตเป็นหลายบรรทัด

การใช้trคุณสามารถแยกอินพุตโดยใช้ตัวคั่นโดยพลการเป็นหลายบรรทัด

อินพุตนี้สามารถถูกเรียกใช้ผ่านsort(ใช้-nถ้าอินพุตเป็นตัวเลข)

หากคุณต้องการเก็บตัวคั่นในเอาต์พุตคุณสามารถใช้trอีกครั้งเพื่อเพิ่มตัวคั่นกลับ

เช่นการใช้พื้นที่เป็นตัวคั่น

cat input.txt | tr " " "\n" | sort -n | tr "\n" " "

อินพุต: 1 2 4 1 4 32 18 3 เอาต์พุต:1 1 2 3 4 4 18 32


คุณอาจสันนิษฐานได้อย่างปลอดภัยว่ารายการตัวเลขและใช่: ตัวคั่นควรถูกแทนที่
Jeff Schaller
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.