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

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

非常に大きな 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

答え1

使用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]
]

使用しawkbash

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

答え2

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

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

説明:

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

答え3

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処理される各引数ごとに arg count によって増分されます。 には、$a次の 2 つの値のいずれかが割り当てられます。
    • a=$((i+=1))- 短いオプションに引数が追加されていない場合、またはオプションが長い場合に割り当てられます。
    • a=$i#-?- オプションが短い場合に割り当てられ、する引数が追加されます。
    • a=\${$a}${1:+$d\${$(($1))\}}- 最初の割り当てに関係なく、$aの値は常に中括弧で囲まれ、場合によっては-sさらに$i1 つ増加され、さらに区切られたフィールドが追加されることがあります。

その結果、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

答え4

見るhttps://stackoverflow.com/questions/12390134/最後の行からコンマを削除します

これは私にとってはうまくいきました:

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

私の最善の方法は、最後の行を削除し、カンマを削除した後、もう一度]文字を追加することです。

関連情報