sed - dosyadaki bir dizenin (virgül) son tekrarını kaldırmak istiyor musunuz?


15

Çok büyük bir csv dosyam var. ,Sonuncuyu sed (veya benzeri) ile nasıl kaldırırsınız ?

...
[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],
]

Istenilen çıktı

...
[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]
]

Aşağıdaki sed komutu satır başına son kez silinir, ancak dosya başına istiyorum.

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

Bu da işe yaramıyor

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

Virgül her zaman ikinci-son satırda mıdır?
John1024

Evet, son satırdan ikinci
spuder

Yanıtlar:


12

kullanma awk

Virgül her zaman ikinci ile son satırın sonundaysa:

$ 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]
]

kullanılması awkvebash

$ 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]
]

kullanma 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 ve diğer BSD platformları için şunları deneyin:

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

kullanma 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"

Belki de bir sed: 1: "x;${s/,$//;p;x}; 2,$ p": extra characters at the end of x command
mac'tayım

@spuder Evet, OSX'in BSD'si vardır sedve genellikle ince şekillerde farklıdır. Bunu test etmek için sed -n -e x -e '${s/,$//;p;x;}' -e '2,$ p' input
OSX'e erişimim yok

Evet, ikincisi Mac'te çalıştı
spuder

4

Sadece aşağıdaki Perl tek hat komutunu deneyebilirsiniz.

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

Açıklama:

  • , Virgülle eşleşir.
  • (?!.*,)Negatif ileri bakış, eşleşen virgülden sonra virgül olmayacağını söyler. Yani son virgülle eşleşir.
  • sVe en önemli solan nokta, yeni satır karakterleri ile bile nokta eşleşmesini sağlayan DOTALL değiştiricisidir.

2
Ayrıca yapabilirdi: perl -0777 -pi -e 's/(.*),(.*?)/\1\2/s'. Bu işe .*yarar çünkü birincisi açgözlüdür, ikincisi değildir.
Oleg Vaskevich

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

Bu ,, herhangi bir giriş dosyasındaki a'nın son tekrarını kaldırmalıdır ve a'nın ,oluşmadığı dosyaları da yazdırır . Temel olarak, virgül içermeyen çizgi dizilerini tamponlar.

Bu virgül karşılaştığında tutma tamponu ile ve bu şekilde aynı zamanda, son virgül itibaren meydana gelen tüm satırları yazdırır içinde mevcut hat tamponu değiştirir ve onun tutma tampon serbest bırakır.

Sadece geçmiş dosyamı inceliyordum ve bunu buldum:

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
)\"";}

Aslında oldukça iyi. Evet, kullanır eval, ancak argümanlarına sayısal bir referansın ötesinde hiçbir şey iletmez. sedSon eşleşmeyi işlemek için rasgele komut dosyaları oluşturur . Sana göstereceğim:

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...'                     

Bu stderr için aşağıdaki yazdırır. Bu, lmatchgirdisinin bir kopyasıdır :

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'

İşlevin evaled alt kabuğu, tüm bağımsız değişkenleri bir kez yineler. Onlar üzerinde yürürken, her bir anahtarın içeriğine bağlı olarak uygun bir sayaç yineler ve bir sonraki yineleme için bu birçok argümanı atlar. O andan itibaren, argüman başına birkaç şeyden birini yapar:

  • Her seçenek için opsiyon ayrıştırıcı ekler $aiçin $o. $adeğeri, $iişlenen her arg için arg sayısı ile artırılan değere göre atanır . $a, aşağıdaki iki değerden birine atanır:
    • a=$((i+=1)) - kısa seçeneklerden birinin argümanı kendisine eklenmemişse veya seçenek uzunsa bu atanır.
    • a=$i#-?- seçenek kısa biridir ve eğer bu atanır gelmez onun arg kendisine eklenmiş olması.
    • a=\${$a}${1:+$d\${$(($1))\}}- İlk atamadan bağımsız olarak, $adeğeri her zaman parantez içine alınır ve - bir -sdurumda - bazen $ibir kez daha artırılır ve ek olarak sınırlandırılmış alan eklenir.

Sonuç olarak, evalbilinmeyenleri içeren bir dize asla geçirilmez. Komut satırı bağımsız değişkenlerinin her birine sayısal bağımsız değişken numaraları denir - hatta ilk bağımsız değişkenin ilk karakterinden ayıklanan ve kaçınılmaz olan karakteri kullanmanız gereken tek zamandır. Temelde, fonksiyon makro jeneratör - herhangi özel bir şekilde argümanların değerleri yorumladığı çünkü hiçbir zaman sedteneke (ve tabii ki olacak) kolaylıkla senaryoyu ayrıştırır zaman hallederim. Bunun yerine, argümanlarını mantıklı bir şekilde çalıştırılabilir bir senaryoda düzenler.

İşte işlevin bazı hata ayıklama çıktısı:

... 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
        '

Ve böylece lmatchbir dosyadaki son eşleşmeyi takiben verilere normal ifadeleri kolayca uygulamak için kullanılabilir. Yukarıda çalıştırdığım komutun sonucu:

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'

... son kez /^.0/izlenen dosya girdisinin alt kümesi eşleştiğinde aşağıdaki ikameleri uygular:

  • sdd&&&&d- yerine $match4 kez geçer .
  • sd'dsqd4 - son maçtan bu yana hattın başlangıcını takip eden dördüncü tek tırnak.
  • sd"d\dqd2 - Aynen, ama çift tırnak ve küresel için.

Ve böylece, lmatchbir dosyadaki son virgül kaldırmak için nasıl kullanılabileceğini göstermek için:

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

ÇIKTI:

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

1
@ don_crissti - şimdi çok daha iyi - -mSeçeneği bıraktım ve zorunlu kıldım , re ve repl için birden fazla argümana geçtim -sve uygun sınırlayıcı işlemeyi de uyguladım. Bence kurşun geçirmez. Başarılı bir şekilde hem boşluk hem de tek bir alıntıyı sınırlayıcı olarak kullandım,
mikeserv

2

Virgül ikinci-son satırda olmayabilir

Kullanılması awkve tac:

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

awkKomut değiştirme modelini görülen ilk kez yapılacak basit bir tanesidir.  tacdosyadaki satırların sırasını tersine çevirir, böylece awkkomut son virgül kaldırılır .

Bana söylendi

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

daha verimli olabilir.


2

Kullanabiliyorsanız tac:

tac file | perl -pe '$_=reverse;!$done && s/,// && $done++;$_=reverse'|tac

1

bkz. /programming/12390134/remove-comma-from-last-line

Bu benim için çalıştı:

$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"}

Benim en iyi yolu son satırı kaldırmak ve virgül kaldırdıktan sonra,] char tekrar ekleyin


1

Aşağıdakileri deneyin vi:

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

Açıklama:

  • $-1 ikinci satırdan son satıra kadar seç

  • s değiştirmek

  • \(,\)\(\_s*]\)virgül ]ve ardından boşluk veya satırsonu ile ayrılmış olarak bulun
  • \2\(\_s*]\)boşluk veya satırsonu ile değiştirin ve ardından]

-1

Aşağıdaki sedkomutu deneyin .

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

1
Bu, her satırdan tramvay virgülünü kaldıracaktır , bu OP istemez.
Archemar

@Archemar Hayır, yalnızca son satırda kaldırılacak, ancak OP'nin son satırda olmayan verileri için çalışmaz
αғsнιη
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.