एक फ़ाइल में एक स्ट्रिंग (एक अल्पविराम) की बहुत अंतिम घटना को हटा दें?


15

मेरे पास एक बहुत बड़ी सीएसवी फ़ाइल है। आप ,sed (या समान) के साथ अंतिम को कैसे निकालेंगे ?

...
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0],
]

वांछित उत्पादन

...
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

निम्न sed कमांड प्रति पंक्ति अंतिम घटना को हटा देगा, लेकिन मुझे प्रति फ़ाइल चाहिए।

sed -e 's/,$//' foo.csv

न ही यह काम करता है

sed '$s/,//' foo.csv

क्या अल्पविराम हमेशा दूसरी-से-अंतिम पंक्ति में होता है?
जॉन 1024

हां, दूसरी पंक्ति से दूसरी पंक्ति तक
spuder

जवाबों:


12

का उपयोग करते हुए awk

यदि अल्पविराम हमेशा दूसरी से अंतिम पंक्ति में होता है:

$ awk 'NR>2{print a;} {a=b; b=$0} END{sub(/,$/, "", a); print a;print b;}'  input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

का उपयोग कर awkऔरbash

$ awk -v "line=$(($(wc -l <input)-1))" 'NR==line{sub(/,$/, "")} 1'  input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

का उपयोग करते हुए sed

$ sed 'x;${s/,$//;p;x;};1d'  input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

OSX और अन्य BSD प्लेटफार्मों के लिए, प्रयास करें:

sed -e x -e '$ {s/,$//;p;x;}' -e 1d  input

का उपयोग करते हुए bash

while IFS=  read -r line
do
    [ "$a" ] && printf "%s\n" "$a"
    a=$b
    b=$line
done <input
printf "%s\n" "${a%,}"
printf "%s\n" "$b"

शायद इसकी वजह से मैं एक मैक पर हूँ, लेकिन sed कमांड त्रुटि देता हैsed: 1: "x;${s/,$//;p;x}; 2,$ p": extra characters at the end of x command
spuder

@spuder हां, OSX में BSD है sedऔर यह अक्सर सूक्ष्म तरीकों से अलग होता है। इस का परीक्षण करने के लिए मेरे पास OSX तक नहीं है, लेकिन कृपया कोशिश करेंsed -n -e x -e '${s/,$//;p;x;}' -e '2,$ p' input
John1024

हां, उस दूसरे ने मैक पर काम किया
स्पूडर

4

बस आप नीचे दिए गए पर्ल वन-लाइनर कमांड को आजमा सकते हैं।

perl -00pe 's/,(?!.*,)//s' file

स्पष्टीकरण:

  • , एक अल्पविराम से मेल खाता है।
  • (?!.*,)नकारात्मक लुकहैड का दावा है कि उस कॉमा के मिलान के बाद अल्पविराम नहीं होगा। तो यह अंतिम अल्पविराम से मेल खाता है।
  • sऔर सबसे ज्यादा आयात करने वाली चीज़ sDOTALL संशोधक है जो डॉट को मैच करती है यहाँ तक कि newline वर्णों से भी मेल खाती है।

2
आप भी कर सकते हैं perl -0777 -pi -e 's/(.*),(.*?)/\1\2/s':। यह काम करता है क्योंकि पहला .*लालची है, जबकि दूसरा नहीं है।
ओलेग वास्केविच

4
lcomma() { sed '
    $x;$G;/\(.*\),/!H;//!{$!d
};  $!x;$s//\1/;s/^\n//'
}

कि ,किसी भी इनपुट फ़ाइल में केवल एक अंतिम घटना को हटा दिया जाना चाहिए - और यह अभी भी उन लोगों को प्रिंट करेगा जिसमें ,ऐसा नहीं होता है। मूल रूप से, यह उन लाइनों के दृश्यों को बफ़र करता है जिनमें कॉमा नहीं होता है।

जब यह एक अल्पविराम का सामना करता है तो यह वर्तमान लाइन बफर को होल्ड बफर के साथ स्वैप करता है और इस तरह से एक साथ सभी लाइनों को प्रिंट करता है जो पिछले अल्पविराम के बाद हुई थी और इसके होल्ड बफर को मुक्त करती है।

मैं सिर्फ अपनी इतिहास फ़ाइल के माध्यम से खुदाई कर रहा था और यह पाया:

lmatch(){ set "USAGE:\
        lmatch /BRE [-(((s|-sub) BRE)|(r|-ref)) REPL [-(f|-flag) FLAG]*]*
"       "${1%"${1#?}"}" "$@"
        eval "${ZSH_VERSION:+emulate sh}"; eval '
        sed "   1x;     \\$3$2!{1!H;\$!d
                };      \\$3$2{x;1!p;\$!d;x
                };      \\$3$2!x;\\$3$2!b'"
        $(      unset h;i=3 p=:-:shfr e='\033[' m=$(($#+1)) f=OPTERR
                [ -t 2 ] && f=$e\2K$e'1;41;17m}\r${h-'$f$e\0m
                f='\${$m?"\"${h-'$f':\t\${$i$e\n}\$1\""}\\c' e=} _o=
                o(){    IFS=\ ;getopts  $p a "$1"       &&
                        [ -n "${a#[?:]}" ]              &&
                        o=${a#-}${OPTARG-${1#-?}}       ||
                        ! eval "o=$f;o=\${o%%*\{$m\}*}"
        };      a(){    case ${a#[!-]}$o in (?|-*) a=;;esac; o=
                        set $* "${3-$2$}{$((i+=!${#a}))${a:+#-?}}"\
                                ${3+$2 "{$((i+=1))$e"} $2
                        IFS=$;  _o=${_o%"${3+$_o} "*}$*\
        };      while   eval "o \"\${$((i+=(OPTIND=1)))}\""
                do      case            ${o#[!$a]}      in
                        (s*|ub)         a s 2 ''        ;;
                        (r*|ef)         a s 2           ;;
                        (f*|lag)        a               ;;
                        (h*|elp)        h= o; break     ;;
                esac;   done;   set -f; printf  "\t%b\n\t" $o $_o
)\"";}

यह वास्तव में बहुत अच्छा है। हां, यह उपयोग करता है eval, लेकिन यह अपने तर्कों के संख्यात्मक संदर्भ से परे कभी भी इसे पारित नहीं करता है। यह sedएक आखिरी मैच को संभालने के लिए मनमाना स्क्रिप्ट बनाता है । मैं तुम्हें दिखाता हूँ:

printf "%d\" %d' %d\" %d'\n" $(seq 5 5 200) |                               
    tee /dev/fd/2 |                                                         
    lmatch  d^.0     \  #all re's delimit w/ d now                           
        -r '&&&&'    \  #-r or --ref like: '...s//$ref/...'      
        --sub \' sq  \  #-s or --sub like: '...s/$arg1/$arg2/...'
        --flag 4     \  #-f or --flag appended to last -r or -s
        -s\" \\dq    \  #short opts can be '-s $arg1 $arg2' or '-r$arg1'
        -fg             #tacked on so: '...s/"/dq/g...'                     

यह निम्नलिखित को stderr पर प्रिंट करता है। यह एक lmatchइनपुट है:

5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
105" 110' 115" 120'
125" 130' 135" 140'
145" 150' 155" 160'
165" 170' 175" 180'
185" 190' 195" 200'

इस फंक्शन का evalसंस्करण एक बार अपने सभी तर्कों के माध्यम से पुन: प्रसारित हो जाता है। जैसा कि यह उन पर चलता है यह प्रत्येक स्विच के संदर्भ के आधार पर एक काउंटर को उचित रूप से पुनरावृत्त करता है और अगले पुनरावृत्ति के लिए कई तर्कों को छोड़ देता है। तब से यह प्रति तर्क कुछ चीजों में से एक करता है:

  • प्रत्येक विकल्प के लिए विकल्प पार्सर जोड़ता $aहै $o। संसाधित किए गए प्रत्येक arg के लिए arg count के द्वारा बढ़ाए गए $aमूल्य के आधार पर असाइन किया गया $iहै। $aनिम्नलिखित दो में से एक मान दिया गया है:
    • a=$((i+=1)) - यह निर्धारित किया जाता है यदि या तो एक लघु-विकल्प के पास इसके तर्क को जोड़ा नहीं जाता है या यदि विकल्प एक लंबा था।
    • a=$i#-?- इस विकल्प अगर एक छोटी से एक है और असाइन किया गया है है इसके आर्ग इसके परिशिष्ट में होगा।
    • a=\${$a}${1:+$d\${$(($1))\}}- प्रारंभिक असाइनमेंट के बावजूद, $a'' का मान हमेशा ब्रेसिज़ में लिपटा होता है और - एक -sमामले में - कभी-कभी $iएक और बढ़ जाता है और इसके अतिरिक्त सीमांकित क्षेत्र को जोड़ दिया जाता है।

नतीजा यह है कि evalकिसी भी अज्ञात से युक्त स्ट्रिंग को कभी पास नहीं किया जाता है। कमांड-लाइन तर्कों में से प्रत्येक को उनके संख्यात्मक तर्क संख्या द्वारा संदर्भित किया जाता है - यहां तक ​​कि सीमांकक जो पहले तर्क के पहले चरित्र से निकाला जाता है और केवल समय है जो आपको किसी भी वर्ण का उपयोग करना चाहिए जो कि अपठित है। मूल रूप से, समारोह एक मैक्रो जनरेटर है - यह किसी भी विशेष तरीके से बहस 'मूल्यों की व्याख्या कभी नहीं क्योंकि sedकर सकते हैं (और, ज़ाहिर है जाएगा) आसानी से जब यह स्क्रिप्ट को पार्स करता है कि संभाल। इसके बजाय, यह समझदारी से अपने आर्गन्स को एक काम करने योग्य स्क्रिप्ट में व्यवस्थित करता है।

यहाँ काम पर समारोह के कुछ डिबग उत्पादन है:

... sed "   1x;\\$2$1!{1!H;\$!d
        };      \\$2$1{x;1!p;\$!d;x
        };      \\$2$1!x;\\$2$1!b
        s$1$1${4}$1
        s$1${6}$1${7}$1${9}
        s$1${10#-?}$1${11}$1${12#-?}
        "
++ sed '        1x;\d^.0d!{1!H;$!d
        };      \d^.0d{x;1!p;$!d;x
        };      \d^.0d!x;\d^.0d!b
        sdd&&&&d
        sd'\''dsqd4
        sd"d\dqdg
        '

और इसलिए lmatchआसानी से फ़ाइल में अंतिम मैच के बाद डेटा को आसानी से लागू करने के लिए इस्तेमाल किया जा सकता है। मेरे द्वारा ऊपर दी गई कमांड का परिणाम है:

5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
101010105dq 110' 115dq 120'
125dq 130' 135dq 140sq
145dq 150' 155dq 160'
165dq 170' 175dq 180'
185dq 190' 195dq 200'

... जो, पिछली बार /^.0/के मिलान के बाद फ़ाइल इनपुट के सबसेट को देखते हुए , निम्नलिखित प्रतिस्थापन को लागू करता है:

  • sdd&&&&d- $matchखुद के साथ 4 बार बदलता है ।
  • sd'dsqd4 - पिछले मैच के बाद से लाइन की शुरुआत के बाद चौथा एकल-उद्धरण।
  • sd"d\dqd2 - डिट्टो, लेकिन विश्व स्तर पर दोहरे उद्धरण चिह्नों के लिए।

और इसलिए, यह प्रदर्शित lmatchकरने के लिए कि कोई फ़ाइल में अंतिम अल्पविराम को हटाने के लिए कैसे उपयोग कर सकता है :

printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1

उत्पादन:

5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100

1
@don_crissti - यह अब बेहतर है - मैंने -mविकल्प को छोड़ दिया और इसे अनिवार्य बना दिया, फिर से और उत्तर के लिए कई तर्कों पर स्विच किया -sऔर उचित सीमांकित हैंडलिंग को भी लागू किया। मुझे लगता है कि यह बुलेट प्रूफ है। मैंने सफलतापूर्वक एक स्थान और एक ही उद्धरण दोनों को सीमांकक के रूप में इस्तेमाल किया,
mikeserv

2

यदि अल्पविराम दूसरी-से-अंतिम पंक्ति में न हो

उपयोग करना awkऔर tac:

tac foo.csv | awk '/,$/ && !handled { sub(/,$/, ""); handled++ } {print}' | tac

awkआदेश प्रतिस्थापन पहली बार पैटर्न में देखा जाता है ऐसा करने के लिए एक सरल एक है।  tacफ़ाइल में लाइनों के क्रम को उलट देता है, इसलिए अंतिम कॉमा awkको हटाकर कमांड समाप्त हो जाती है ।

मुझे बताया गया है कि

tac foo.csv | awk '/,$/ && !handled { sub(/,$/, ""); handled++ } {print}' > tmp && tac tmp

अधिक कुशल हो सकता है।



1

देख /programming/12390134/remove-comma-from-last-line

यह मेरे लिए काम किया है:

$cat input.txt
{"name": "secondary_ua","type":"STRING"},
{"name": "request_ip","type":"STRING"},
{"name": "cb","type":"STRING"},
$ sed '$s/,$//' < input.txt >output.txt
$cat output.txt
{"name": "secondary_ua","type":"STRING"},
{"name": "request_ip","type":"STRING"},
{"name": "cb","type":"STRING"}

मेरा सबसे अच्छा तरीका है कि अंतिम पंक्ति को हटा दें और अल्पविराम को हटाने के बाद] फिर से चार को जोड़ें


1

नीचे के साथ कोशिश करें vi:

  vi "+:$-1s/\(,\)\(\_s*]\)/\2/e" "+:x" file

स्पष्टीकरण:

  • $-1 दूसरी अंतिम पंक्ति का चयन करें

  • s बदलने के

  • \(,\)\(\_s*]\)इसके बाद ]रिक्त स्थान या न्यूलाइन द्वारा अल्पविराम खोजें और अलग करें
  • \2\(\_s*]\)इसके बाद रिक्त स्थान या न्यूलाइन द्वारा प्रतिस्थापित करें]

-1

नीचे दिए गए sedआदेश के साथ प्रयास करें ।

sed -i '$s/,$//' foo.csv

1
यह हर लाइन से कमिंग कॉमा को हटा देगा , यह waht OP नहीं है।
आर्केमर

@Archemar नहीं, यह केवल अंतिम पंक्ति पर निकलेगा, लेकिन यह ओपी के डेटा के लिए काम नहीं करेगा जो अंतिम पंक्ति में नहीं है
α linesнιη
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.