1

我正在使用手动填充文件,但在解析它时遇到了问题。我的文件输入文件无法更改,我的代码语言无法从 bash 脚本更改。

我做了一个简单的例子让你更容易^^

var="hey","i'm","happy, like","you"
IFS="," read -r one two tree for five <<<"$var"
echo $one:$two:$tree:$for:$five

现在我想你已经看到了这里的问题。我想得到

hey:i'm:happy, like:you:

但我明白了

hey:i'm:happy: like:you

我需要一种方法来告诉read“”比 IFS 更重要。我已经阅读了有关该eval命令的信息,但我不能冒这个风险。

最后,这是一个目录文件,麻烦的字段是描述字段,因此它基本上可以包含任何内容。

看起来像这样的原始文件

"type","cn","uid","gid","gecos","description","timestamp","disabled"
"type","cn","uid","gid","gecos","description","timestamp","disabled"
"type","cn","uid","gid","gecos","description","timestamp","disabled"

编辑#1

我会举一个更好的例子;我上面使用的那个太简单了,@StefanHegny 发现它会导致另一个错误。

while read -r ldapLine
    do
            IFS=',' read -r objectClass dumy1 uidNumber gidNumber username description modifyTimestamp nsAccountLock gecos homeDirectory loginShell createTimestamp dumy2 <<<"$ldapLine"

            isANetuser=0

            while IFS=":" read -r -a class
            do
                    for i in "${class[@]}"
                    do
                            if [ "$i" == "account" ]
                            then
                                    isANetuser=1
                                    break
                            fi
                    done
            done <<< $objectClass

            if [ $isANetuser == 0 ]
            then
                    continue
            fi

            #MORE STUFF APPEND#

    done < file.csv

所以这是代码的一小部分,但它应该解释我的工作。有file.csv很多这样的行:

"top:shadowAccount:account:posixAccount","Jdupon","12345","6789","Jdupon","Jean Mark, Dupon","20140511083750Z","","Jean Mark, Dupon","/home/user/Jdupon","/bin/ksh","20120512083750Z","",""
4

2 回答 2

2

如果bash您将使用的各种版本都比 v3.0 更新,那么当BASH_REMATCH引入正则表达式和时,您可以使用类似以下函数的东西:[注 1]

each_field () {
    local v=,$1;
    while [[ $v =~ ^,(([^\",]*)|\"[^\"]*\") ]]; do
        printf "%s\n" "${BASH_REMATCH[2]:-${BASH_REMATCH[1]:1:-1}}";
        v=${v:${#BASH_REMATCH[0]}};
    done
}

它的参数是一行(记住要引用它!),它将每个逗号分隔的字段打印在单独的行上。如所写,它假定没有字段包含换行符;这在 CSV 中是合法的,但它使得将文件分成多行变得更加复杂。如果您确实需要处理这种情况,您可以\n将 printf 语句中的 更改为 a \0,然后使用类似的东西xargs -0来处理输出。(或者您可以在该字段中插入您需要做的任何处理来代替printf语句。)

在不修改未引用字段的情况下取消引用引用字段会遇到一些麻烦。但是,它会在嵌入双引号的字段上失败。如有必要,这是可以修复的。[笔记2]

这是一个示例,以防不明显:

while IFS= read -r line; do
  each_field "$line"
  printf "%s\n" "-----"
done <<EOF
type,cn,uid,gid,gecos,"description",timestamp,disabled
"top:shadowAccount:account:posixAccount","Jdupon","12345","6789","Jdupon","Jean Mark, Dupon","20140511083750Z","","Jean Mark, Dupon","/home/user/Jdupon","/bin/ksh","20120512083750Z","",""

EOF

输出:

type
cn
uid
gid
gecos
description
timestamp
disabled
-----
top:shadowAccount:account:posixAccount
Jdupon
12345
6789
Jdupon
Jean Mark, Dupon
20140511083750Z

Jean Mark, Dupon
/home/user/Jdupon
/bin/ksh
20120512083750Z


-----

笔记:

  1. 我不是说你应该使用这个功能。您应该使用 CSV 解析器,或包含良好 CSV 解析库的语言,例如 python。但我相信这个 bash 函数可以在某种常见 CSV 方言的正确格式的 CSV 文件上工作,尽管速度很慢。

  2. 这是一个处理带引号字段内的双引号的版本,这是用于内部引号的经典 CSV 语法:

    each_field () { 
        local v=,$1;
        while [[ $v =~ ^,(([^\",]*)|\"(([^\"]|\"\")*)\") ]]; do
            echo "${BASH_REMATCH[2]:-${BASH_REMATCH[3]//\"\"/\"}}";
            v=${v:${#BASH_REMATCH[0]}};
        done
    }
    
于 2016-05-25T15:15:45.863 回答
0

我的建议,如以前的一些答案(见下文),是将分隔符切换为|(并IFS="|"改用):

sed -r 's/,([^,"]*|"[^"]*")/|\1/g'

但是,这需要sed具有扩展正则表达式 ( -r) 的 a。

我应该使用 AWK 还是 SED 从 CSV 文件中删除引号之间的逗号?(重击)

是否可以编写一个匹配特定模式的正则表达式,然后用模式的一部分进行替换

于 2016-05-25T13:48:28.017 回答