sed-ファイル内の文字列(カンマ)の最後の出現を削除しますか?


15

非常に大きなcsvファイルがあります。最後の,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

コンマは常に最後から2番目の行にありますか?
John1024 14年

はい、最後から2番目の行
14年

回答:


12

を使用して awk

コンマが常に2番目から最後の行の最後にある場合:

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

たぶん、私はMacを使用しているのですが、sedコマンドでエラーが発生しますsed: 1: "x;${s/,$//;p;x}; 2,$ p": extra characters at the end of x command
spuder 14年

@spuderはい、OSXにはBSDがsedあり、多くの場合微妙に異なっています。私はこれをテストするためにOSXへのアクセス権を持っていますが、試してみてくださいませんsed -n -e x -e '${s/,$//;p;x;}' -e '2,$ p' input
John1024

はい、その2つ目はMacで動作しました
14年

4

以下のPerlワンライナーコマンドを試すことができます。

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

説明:

  • , コンマに一致します。
  • (?!.*,)負の先読みは、コンマに一致した後にコンマがないことを表明します。したがって、最後のコンマと一致します。
  • sそして、最も重要なのはs、ドットを改行文字にも一致させるDOTALL修飾子です。

2
次のこともできますperl -0777 -pi -e 's/(.*),(.*?)/\1\2/s'。これが機能するのは、最初のもの.*は貪欲であり、2番目のものはそうではないからです。
オレグヴァスケビッチ

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

これにより、,入力ファイル内で最後に出現するaのみが削除され、a ,が出現しないものも引き続き出力されます。基本的に、コンマを含まない行のシーケンスをバッファします。

それはコンマに遭遇したとき、それは保持バッファとし、その方法は同時に、最後のコンマ以降に発生したすべての行出力し、現在では、ラインバッファスワップおよびその保持バッファを解放します。

私は自分の履歴ファイルを掘り下げていましたが、これが見つかりました:

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'

関数のevaledサブシェルは、すべての引数を1回繰り返します。それらを調べると、各スイッチのコンテキストに応じて適切にカウンターを反復し、次の反復のためにその数の引数をスキップします。それ以降は、引数ごとにいくつかの処理のいずれかを実行します。

  • オプションごとに、オプションパーサーがに追加$a$oます。処理された各引数の引数カウントによってインクリメントされる$a値に基づいて割り当てられ$iます。$a次の2つの値のいずれかが割り当てられます。
    • a=$((i+=1)) -これは、短いオプションに引数が追加されていない場合、またはオプションが長いオプションの場合に割り当てられます。
    • a=$i#-?-オプションは、短いもので、この場合は割り当てられないそのargは、それに添付されています。
    • a=\${$a}${1:+$d\${$(($1))\}}-最初の割り当てに関係なく、$aの値は常に中括弧で囲まれ、-s場合によって$iはもう1つインクリメントされ、さらに区切りフィールドが追加されます。

その結果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 -最後に一致してから行の先頭に続く4番目の単一引用符。
  • 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オプションを削除し、必須にし、reおよびreplの複数の引数に切り替え、-s適切な区切り文字処理も実装しました。私はそれが防弾だと思う。スペースと単一引用符の両方を区切り文字として使用できました。
mikeserv

2

コンマが最後から2番目の行にない場合

awkand を使用tac

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

このawkコマンドは、パターンが初めて表示されたときに置換を実行する簡単なコマンドです。  tacファイル内の行の順序が逆になるため、awkコマンドは最後のコンマを削除します。

私はそれを言われました

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

より効率的かもしれません。


2

使用できる場合tac

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

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

私の最善の方法は、最後の行を削除し、カンマを削除した後、再度] charを追加することです


1

以下で試してくださいvi

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

説明:

  • $-1 最後から2番目の行を選択

  • s 取り替える

  • \(,\)\(\_s*]\)]スペースまたは改行で区切られたコンマを見つけます
  • \2\(\_s*]\)ieスペースまたは改行で置き換え、その後に]

-1

以下のsedコマンドで試してください。

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

1
これにより、すべての行から末尾のカンマが削除されますが、これはOPの望みではありません。
アルケマール

@Archemarいいえ、それは最後の行だけが、最後の行ではありませんOPのデータのためのものではないでしょう仕事に削除されます
αғsнιη
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.