रिक्त स्थान सहित लाइन की लंबाई से एक पाठ फ़ाइल को सॉर्ट करें


137

मेरे पास एक CSV फ़ाइल है जो इस तरह दिखती है

AS2345, ASDF1232, श्री प्लेन उदाहरण, 110 बाइनरी ave।, अटलांटिस, आरआई, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, श्रीमती प्लेन उदाहरण, 1121110 टर्नरी सेंट। 110 बाइनरी एवे .., अटलांटिस, आरआई, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, मिस्टर प्लेन उदाहरण, 110 बाइनरी ave।, लिबर्टी सिटी, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, मिस्टर प्लेन उदाहरण, 110 टर्नरी एवे।, कुछ सिटी, आरआई, 12345, (999) 123-5555,1.56

मुझे रिक्त स्थान सहित लाइन की लंबाई के अनुसार इसे क्रमबद्ध करने की आवश्यकता है। निम्न कमांड में रिक्त स्थान शामिल नहीं है, क्या इसे संशोधित करने का कोई तरीका है ताकि यह मेरे लिए काम करे?

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

21
मैं वास्तव में द्विआधारी एवेन्यू या त्रिगुट स्ट्रीट में रहते हैं, उन लोगों को निश्चित रूप से की तरह चीजों के साथ सहमत होगा चाहते हैं जो "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) एक दूसरे के खिलाफ छंटनी की जा रही लाइनों को रोकने के लिए, और उन्हें उसी क्रम में रखा जाए जिसमें वे इनपुट में होते हैं।

(जो लोग इन संबंधों को छाँटने का अधिक नियंत्रण चाहते हैं, वे इस प्रकार के --keyविकल्प को देख सकते हैं ।)

प्रश्न का हल करने का प्रयास विफल क्यों होता है (awk line-rebuild):

यह ध्यान रखना दिलचस्प है कि इसके बीच का अंतर:

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

वे क्रमशः उपज देते हैं

hello   awk   world
hello awk world

के प्रासंगिक अनुभाग (gawk का) मैनुअल केवल एक अलग रूप में के रूप में कहा गया है कि awk जब आप एक क्षेत्र बदलने (विभाजक के आधार पर, आदि) $ 0 के पूरे के पुनर्निर्माण के लिए जा रहा है। मुझे लगता है कि यह पागल व्यवहार नहीं है। यह इस है:

"अंत में, ऐसे समय होते हैं जब फ़ील्ड और ओएफएस के वर्तमान मूल्य का उपयोग करके, पूरे रिकॉर्ड को फिर से बनाने के लिए जाग को मजबूर करना सुविधाजनक होता है। ऐसा करने के लिए, उचित रूप से अहानिकर असाइनमेंट का उपयोग करें:"

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

"यह बल रिकॉर्ड को फिर से बनाने के लिए जागता है।"

समान लंबाई की कुछ पंक्तियों सहित परीक्षण इनपुट:

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
heemayl, हाँ, यह धन्यवाद है। मैंने ओपी के प्रयास के समाधान के आकार से मेल खाने की कोशिश की है, जहां संभव हो, उसे अपने और मेरे बीच केवल महत्वपूर्ण अंतर पर ध्यान केंद्रित करने में सक्षम बनाने के लिए।
नीलब

1
यह इंगित करने योग्य है कि cat $@टूटी हुई है, भी। आप निश्चित रूप से इसे उद्धृत करना चाहते हैं, जैसेcat "$@"
ट्रिपल जूल

27

Neillb से AWK समाधान महान तुम सच में उपयोग करना चाहते हैं है awkऔर यह बताता है कि क्यों यह एक परेशानी नहीं है, लेकिन अगर आप क्या चाहते हैं काम जल्दी से किया जाना है और परवाह नहीं है क्या आप इसे में करते हैं, एक ही समाधान उपयोग करने के लिए है sort()इनपुट लाइनों पर पुनरावृति करने के लिए एक कस्टम कैपरिसन रूटीन के साथ पर्ल का कार्य। यहाँ एक लाइनर है:

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

आप इसे अपनी पाइपलाइन में रख सकते हैं जहाँ भी आपको इसकी आवश्यकता हो, या तो STDIN प्राप्त कर रहे हैं (से) cat या शेल पुनर्निर्देशित) प्राप्त करना या बस किसी अन्य तर्क के रूप में फ़ाइल नाम देना और इसे फ़ाइल खोलने दें।

मेरे मामले में मुझे सबसे लंबी लाइनों की जरूरत थी, इसलिए मैंने तुलना में $aऔर अदला-बदली की $b


यह बेहतर समाधान है क्योंकि awk अप्रत्याशित छँटाई का कारण बनता है जब इनपुट फ़ाइल में न्यूमेरिक और अल्फ़ान्यूमेरिक लाइनें होती हैं यहाँ ऑनलाइन कमांड: $ कैट टेस्टफाइल | perl -e 'प्रिंट प्रकार {लंबाई ($ a) <=> लंबाई ($ b)} <>'
anlemol

तेज! क्या 465,000 लाइन फ़ाइल (प्रति पंक्ति एक शब्द) <1 सेकंड में, जब आउटपुट किसी अन्य फ़ाइल में रीडायरेक्ट हो गया - इस प्रकार:cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
cssyphus

स्ट्राबेरीपर्ल के साथ विंडोज काम करता है: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 मिलियन लाइन मेंस्ट्रिटी (ब्रिटिश नेशनल कॉर्पस txt) है

परिणाम

  1. कालेब के perlसमाधान में 11.2 सेकंड लगे
  2. मेरे perlसमाधान में 11.6 सेकंड लगे
  3. नीलब के awkसमाधान # 1 को 20 सेकंड लगे
  4. नीलब के awkसमाधान # 2 में 23 सेकंड लगे
  5. शुभ के awkसमाधान में 24 सेकंड लगे
  6. जोनाथन के awkसमाधान में 25 सेकंड लगे
  7. Fretz का bashसमाधान समाधानों की तुलना में 400x अधिक लंबा होता है awk(100000 लाइनों के एक काटे गए परीक्षण मामले का उपयोग करके)। यह ठीक काम करता है, बस हमेशा के लिए लेता है।

अतिरिक्त 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) की उप-छँटाई को कैसे प्रतिबंधित किया जाए, इसका पता लगाया है । आप का क्या मतलब था या नहीं, इसे मेरे ध्यान में लाने के लिए धन्यवाद! मैंने परीक्षण करने के लिए एक माना हुआ इनपुट भी जोड़ा है।
नीलब

4
नहीं, मुझे इसे तोड़कर समझाएं। बस awkभाग लाइन की लंबाई और एक स्थान के साथ उपसर्गित लाइनों की एक सूची उत्पन्न करेगा। sort -nउम्मीद के मुताबिक काम करना होगा। लेकिन अगर उन पंक्तियों में से किसी पर पहले से ही एक नंबर है, तो वे लाइनें लंबाई + स्थान + संख्या के साथ शुरू होंगी। sort -nउस स्थान की अवहेलना करता है और इसे लंबाई + संख्या से एक संख्या के रूप में माना जाएगा। -gइसके बजाय ध्वज का उपयोग करना पहले स्थान पर रुक जाएगा, एक सही प्रकार की उपज होगी। कुछ संख्या-उपसर्ग लाइनों के साथ एक फ़ाइल बनाकर इसे स्वयं आज़माएं और चरण दर चरण कमांड चलाएं।
मार्कस अमलथिया मैग्नसन

1
मैंने यह भी पाया कि sort -nअंतरिक्ष की अवहेलना और एक गलत छँटाई का उत्पादन करता है। sort -gसही क्रम आउटपुट।
रॉबर्ट स्मिथ

मैं के साथ वर्णित समस्या को पुनः नहीं कर सकते -nमें sort (GNU coreutils) 8.21infoप्रलेखन का वर्णन करता है -gकम कुशल और संभवतः कम-सटीक (यह तैरता के लिए संख्याओं) के रूप में है, तो शायद उसका उपयोग नहीं करते, तो आप की जरूरत नहीं है।
फिल्स

nb प्रलेखन के लिए -n: "संख्यात्मक रूप से क्रमबद्ध करें। संख्या प्रत्येक पंक्ति से शुरू होती है और इसमें वैकल्पिक रिक्त स्थान, एक वैकल्पिक '-' चिह्न, और शून्य या अधिक अंक संभवत: हजारों विभाजकों द्वारा अलग किए जाते हैं, वैकल्पिक रूप से दशमलव-बिंदु वर्ण और शून्य या अधिक वर्णों द्वारा पीछा किया जाता है। एक खाली नंबर को '0' के रूप में माना जाता है। 'LC_NUMERIC' लोकेल दशमलव-बिंदु वर्ण और हजारों विभाजक को निर्दिष्ट करता है। डिफ़ॉल्ट रूप से एक रिक्त स्थान या एक टैब है, लेकिन 'LC_CTYPE' स्थान इसे बदल सकता है। "
फिल्स


2

1) शुद्ध awk समाधान। मान लीजिए कि लाइन की लंबाई अधिक नहीं हो सकती है 1024 फिर

बिल्ली का नाम | awk 'बेगिन {मिनट = 1024; s = "";} {l = लंबाई ($ 0); if (l <min) {min = l; s = $ 0?;}} END {प्रिंट s} '

2) सभी लाइन मानने वाले एक लाइनर बैश समाधान में सिर्फ 1 शब्द है, लेकिन किसी भी मामले के लिए फिर से काम किया जा सकता है जहां सभी लाइनों में समान संख्या में शब्द हैं:

लाइनें = $ (बिल्ली का नाम); $ LINES में k के लिए; प्रिंटफ "$ k" करें; इको $ k | wc -L; हो गया | सॉर्ट -k2 | सिर-एन १ | cut -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 variable में प्रत्येक लाइन की एक प्रति बनाता है lऔर हर बार डबल-एस्क्यू करता है 'ताकि लाइन को शेल कमांड के रूप में प्रतिध्वनित किया जा सके ( \047यह ऑक्टल नोटेशन में एकल-उद्धरण है)।
  • cmd=sprintf("echo \047%s\047 | wc -m", l);Exec यह वह कमांड है जिसे हम निष्पादित करेंगे, जो बची हुई लाइन को प्रतिध्वनित करता है wc -m
  • cmd | getline c;← कमांड को निष्पादित करता है और वर्ण गणना मान को कॉपी करता है जिसे awk चर में लौटाया जाता है c
  • close(cmd); । एक प्रक्रिया में खुली फाइलों की संख्या पर एक सिस्टम सीमा से टकराने से बचने के लिए शेल कमांड पर पाइप को बंद करें।
  • sub(/ */, "", c);Value कैरेक्टर काउंट वैल्यू से व्हाइट स्पेस को ट्रिम कर देता है wc
  • { print c, $0 } ← लाइन के कैरेक्टर काउंट वैल्यू, स्पेस और मूल लाइन को प्रिंट करता है।
  • | sort -ns← लाइनों को -nक्रमबद्ध (पूर्व निर्धारित वर्ण गणना मूल्यों द्वारा) संख्यात्मक रूप से ( ), और स्थिर क्रम क्रम ( -s) बनाए रखना ।
  • | cut -d" " -f2- Ended पूर्वनिर्मित वर्ण गणना मानों को हटा देता है।

यह धीमी है (एक तेज़ मैकबुक प्रो पर केवल 160 लाइनें प्रति सेकंड) क्योंकि इसे प्रत्येक पंक्ति के लिए एक उप-कमांड निष्पादित करना होगा।

वैकल्पिक रूप से, इसे केवल पूर्ण gawkरूप से करें (संस्करण 3.1.5 के रूप में, gawk मल्टीबाइट अवगत है), जो काफी तेज होगा। यह सभी भागने और डबल-उद्धृत करने में बहुत परेशानी है, जो कि एक कमांड से शेल कमांड के माध्यम से सुरक्षित रूप से गुजरने के लिए है, लेकिन यह एकमात्र तरीका है जो मुझे मिल सकता है कि अतिरिक्त सॉफ़्टवेयर स्थापित करने की आवश्यकता नहीं है (gawk डिफ़ॉल्ट रूप से उपलब्ध नहीं है मैक ओ एस)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.