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

쉼표가 항상 마지막 줄에 있습니까?
John1024

예, 마지막 줄에 두 번째
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: 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

예, 두 번째는 Mac에서 일한
spuder

4

간단히 아래 Perl one-liner 명령을 시도해보십시오.

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

설명:

  • , 쉼표와 일치합니다.
  • (?!.*,)부정적 예측은 일치하는 쉼표 뒤에 쉼표가 없을 것이라고 주장합니다. 따라서 마지막 쉼표와 일치합니다.
  • s그리고 가장 중요한 것은 s점을 개행 문자와도 일치시키는 DOTALL 수정 자입니다.

2
당신은 또한 할 수 있습니다 : perl -0777 -pi -e 's/(.*),(.*?)/\1\2/s'. 첫 번째 .*는 탐욕스럽고 두 번째는 그렇지 않기 때문에 작동합니다 .
Oleg Vaskevich

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'

함수의 evaled 서브 쉘은 모든 인수를 한 번 반복합니다. 그것들을 살펴보면 각 스위치의 컨텍스트에 따라 카운터를 적절하게 반복하고 다음 반복에 대한 많은 인수를 건너 뜁니다. 그때부터는 논쟁 당 몇 가지 일 중 하나를 수행합니다.

  • 각 옵션마다 옵션 파서가 추가 $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 -ditto, 그러나 큰 따옴표 및 전 세계적으로 사용됩니다.

따라서 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

쉼표가 두 번째 줄에서 마지막 줄에 없을 수 있습니다

사용 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

더 효율적일 수 있습니다.


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

최선의 방법은 마지막 줄을 제거하고 쉼표를 제거한 후] 문자를 다시 추가하는 것입니다.


1

아래에서 시도하십시오 vi:

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

설명:

  • $-1 마지막 줄부터 두 번째 선택

  • s 바꾸다

  • \(,\)\(\_s*]\)쉼표 뒤에 ]공백이나 줄 바꿈으로 구분합니다.
  • \2\(\_s*]\)즉 공백 또는 개행 문자로 대체]

-1

아래 sed명령으로 시도하십시오 .

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

1
이렇게하면 모든 줄 에서 후행 쉼표가 제거 되며 OP가 필요하지는 않습니다.
Archemar

@Archemar 아니요, 마지막 줄에서만 제거되지만 마지막 줄에없는 OP의 데이터에는 적용되지 않습니다
αғsнιη
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.