У меня очень большой файл 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"}
Лучший способ — удалить последнюю строку и после удаления запятой снова добавить символ ]