【发布时间】:2012-05-13 03:55:12
【问题描述】:
我已将此问题移至 code review
我在 bash 中编写了一个递归下降解析器。
我想知道你能否指出一些对我有帮助的东西。它支持反斜杠转义和带解析错误报告的引用字段。
脚本在某些方面类似于cut.. 从文件中获取输入或stdin 允许用户选择他们想要打印的行和字段。分别使用选项-l 和-f。可以打印字段列表,并且可以使用选项 --list '1 2 3 4 5' 和 --list-seperator $'\n' 为该列表指定自定义分隔符。
#!/bin/bash
shopt -s extglob; # we need maxiumum expression capabilities enabled
# option variables
declare list='' delim=$'\n' field='' lineNum=1;
while [[ ${1:0:1} = '-' ]]; do # Parse user arguments
case $1 in
-f)
field=$2; shift 2;
;;
-l)
lineNum=$2; shift 2;
;;
--list-seperator)
delim="$2"; shift 2;
;;
--list)
list="$2"; shift 2;
;;
*) break;;
esac
done
# open a user supplied file on stdin
[[ -e "$1" ]] && exec 0<$1;
# data from 'read/getline'
declare input='';
# we are using sed to optimize input, the command just prints the desired line
read -r input < <(sed -n ${lineNum}p)
# why doesn't the above work as a pipe into read?
# range of this line
declare strLen=${#input} value='';
# data processing variables
declare symbol='' value='';
# REGEX symbol "classes"
declare nothing='' comma='[,]' quote='["]' backslash='[\]' text='[^,\"]';
# integers:
declare -i iPos=-1 tPos=0;
# output array:
declare -a items=();
NextSymbol() {
symbol="${input:$((++iPos)):1}"; # get next char from string
(( iPos < strLen )); # return false if we are out of range
}
Accept() {
[[ -z "$symbol" && -z "$1" ]] && return 0; # accept "nothing/empty"
# if you can meld the above line into the next line
# let me know: pc.wiz.tt@gmail.com; this is some kind of bug!
# becare careful because expect expects 'nothing' to be empty.
# that's why it says 'end of input'
[[ "$symbol" =~ ^$1$ ]] && NextSymbol && return 0; # match symbol
}
Expect() {
Accept "$1" && return
local msg="$0: parse failure: line $lineNum: expected symbol: "
echo "$msg'${1:-end of input}'" >&2;
echo "$0: found: '$symbol'" >&2;
exit 1;
}
value() {
while Accept $text; do # symbol will not be what we expect here
value+=${input:$((iPos-1)):1}; # so get what we expect
done
Accept $nothing && { # this will only happen at end of the string
value+=${input:$((iPos-1)):1} # get the last char
pushValue; # push the data into the array
}
}
pushValue() {
items[tPos++]=$value;
value=''; # clear value!
}
quote() {
until [[ $symbol =~ $quote || -z $symbol ]]; do
value+=$symbol;
NextSymbol;
done
Expect $quote;
}
line() {
Accept $quote && {
quote
line;
}
Accept $backslash && {
value+=$symbol;
NextSymbol;
value;
line;
}
Accept $comma && {
pushValue;
line;
}
Accept $text && {
value=${input:$((iPos-1)):1};
value;
line;
}
}
NextSymbol;
line;
Expect $nothing
[[ $field ]] && { # want field
echo "${items[field-1]}" # print the item
# (index justified for human use)
exit;
}
[[ $list ]] && { # want list
for index in $list; do
echo -n "${items[index-1]}${delim}" # print the list
# (index justified for human use)
done
exit;
}
exit 1; # no field or list
【问题讨论】:
-
@MarcB 不删除,我们可以直接关闭它,让我留下codereview的链接吗?
-
我在侧边栏看到很多关于脚本和 CSV 的问题。即使它是“离题”,这也会很有帮助。
-
只有 43ms 慢。在一个英特尔原子上。在大约 100K 左右的小文件中,用户不会注意到任何内容。
-
请不要交叉发布问题;这样做会给我们在两个网站上带来不必要的麻烦。如果问题不在此处,但与代码审查主题有关,请将其标记为迁移。由于您已将其转发到那里,因此我正在当场关闭它,但我可能不会删除它。
标签: bash csv awk recursive-descent