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

如果逗號始終位於倒數第二行的末端:

$ 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最重要的是sDOTALL 修飾符,它使點甚至可以匹配換行符。

答案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 子 shell 會遍歷其所有參數一次。當它遍歷它們時,它會根據每個開關的上下文適當地迭代計數器,並跳過下一次迭代的許多參數。從那時起,它會對每個參數執行以下操作之一:

  • 對於每個選項,選項解析器都會新增$a$o.$a根據處理的每個 arg 的值$i按 arg count 遞增進行分配。$a被指派以下兩個值之一:
    • a=$((i+=1))- 如果短選項沒有附加其參數或選項是長選項,則指派此選項。
    • a=$i#-?- 如果選項是短選項並且則指派此選項將其 arg 附加到其上。
    • 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

答案4

https://stackoverflow.com/questions/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"}

我最好的方法是刪除最後一行,刪除逗號後,再次添加 ] 字符

相關內容