我有一个非常大的 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],
]
Run Code Online (Sandbox Code Playgroud)
期望输出
...
[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]
]
Run Code Online (Sandbox Code Playgroud)
以下 sed 命令将删除每行的最后一次出现,但我想要每个文件。
sed -e 's/,$//' foo.csv
Run Code Online (Sandbox Code Playgroud)
这也不起作用
sed '$s/,//' foo.csv
Run Code Online (Sandbox Code Playgroud)
Joh*_*024 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]
]
Run Code Online (Sandbox Code Playgroud)
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]
]
Run Code Online (Sandbox Code Playgroud)
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]
]
Run Code Online (Sandbox Code Playgroud)
对于 OSX 和其他 BSD 平台,请尝试:
sed -e x -e '$ {s/,$//;p;x;}' -e 1d input
Run Code Online (Sandbox Code Playgroud)
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"
Run Code Online (Sandbox Code Playgroud)
lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Run Code Online (Sandbox Code Playgroud)
这应该只删除,
任何输入文件中最后一次出现的 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
)\"";}
Run Code Online (Sandbox Code Playgroud)
其实还不错。是的,它使用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...'
Run Code Online (Sandbox Code Playgroud)
将以下内容打印到 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'
Run Code Online (Sandbox Code Playgroud)
该函数的eval
ed 子shell 遍历其所有参数一次。当它遍历它们时,它会根据每个开关的上下文适当地迭代一个计数器,并为下一次迭代跳过那么多参数。从那时起,它会为每个参数执行以下操作之一:
$a
到$o
. $a
是根据$i
每个处理的 arg 的 arg 计数递增的值来分配的。$a
被分配以下两个值之一:
a=$((i+=1))
- 如果短选项没有附加其参数或选项是长选项,则分配此选项。a=$i#-?
- 如果选项是一个简短的选项并且确实附加了它的 arg,则分配此选项。a=\${$a}${1:+$d\${$(($1))\}}
- 无论初始分配如何,$a
的值始终包含在大括号中,并且 - 在某种-s
情况下 - 有时会$i
再增加一个并附加额外的分隔字段。结果是eval
永远不会传递包含任何未知数的字符串。每个命令行参数都由它们的数字参数编号引用 - 甚至是从第一个参数的第一个字符中提取的分隔符,这是您应该使用任何未转义字符的唯一时间。基本上,该函数是一个宏生成器 - 它从不以任何特殊方式解释参数的值,因为在解析脚本时sed
可以(并且当然会)轻松处理它。相反,它只是明智地将其 args 安排成一个可行的脚本。
这是工作中的函数的一些调试输出:
... 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
'
Run Code Online (Sandbox Code Playgroud)
因此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'
Run Code Online (Sandbox Code Playgroud)
...其中,给定最后一次/^.0/
匹配的文件输入的子集,应用以下替换:
sdd&&&&d
-$match
自行替换4 次。sd'dsqd4
- 自上次匹配以来,行首之后的第四个单引号。sd"d\dqd2
- 同上,但适用于双引号和全局。因此,为了演示如何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'
Run Code Online (Sandbox Code Playgroud)
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100
Run Code Online (Sandbox Code Playgroud)
简单地你可以试试下面的 Perl 单行命令。
perl -00pe 's/,(?!.*,)//s' file
Run Code Online (Sandbox Code Playgroud)
解释:
,
匹配逗号。(?!.*,)
负前瞻断言在匹配的逗号之后不会有逗号。所以它会匹配最后一个逗号。s
最重要的是s
DOTALL 修饰符,它使 dot 甚至可以匹配换行符。