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

Используя 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"

решение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'

Подоболочка ed функции evalпроходит по всем своим аргументам один раз. По мере того, как она проходит по ним, она соответствующим образом итерирует счетчик в зависимости от контекста для каждого переключателя и пропускает столько же аргументов для следующей итерации. С этого момента она делает одну из нескольких вещей для каждого аргумента:

  • Для каждой опции анализатор опций добавляет $aк $o. $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- то же самое, но для двойных кавычек и глобально.

Итак, чтобы продемонстрировать, как можно 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"}

Лучший способ — удалить последнюю строку и после удаления запятой снова добавить символ ]

Связанный контент