【问题标题】:how to insert separate lines ( elements ) in parameter如何在参数中插入单独的行(元素)
【发布时间】:2014-09-17 12:26:11
【问题描述】:

我有以下 csv 文件

more file.csv

1,yes,yes,customer1,1,2,3,4
2,no,yes,customer5,34,56,33,2
3,yes,yes,customer11
4,no,no,customer14
5,yes,no,customer15
6,yes,yes,customer21
7,no,yes,customer34
8,no,yes,customer89

编写以下(awk)行是为了操作并从 csv 中获取行并将每个元素(行)放入参数 - LINES

  declare LINES=`  awk -F, 'BEGIN{IGNORECASE=1} $2=="yes"  {printf "\"Line number %d customer %s\"\n", $1, $4}' file.csv `

.

 echo $LINES

"Line number 1 customer customer1" "Line number 3 customer customer11" "Line number 5 customer customer15" "Line number 6 customer 21”

但是当我想在参数 LINES 中打印元素的数量时,我得到 1 ??

echo ${#LINES[*]} 
1

虽然实际上我需要获得 4 个元素(行)

请建议如何修复 awk 行以获得 4 个元素?

备注:

请看这个例子,当我手动编辑LINES时,元素应该是4个

     declare LINES=(  "Line number 1 customer customer1" "Line number 3 customer customer11" "Line number 5 customer customer15" "Line number 6 customer 21” )


echo ${#LINES[*]}

4

【问题讨论】:

    标签: linux bash shell awk


    【解决方案1】:

    awk 输出未存储在数组中。你需要declare -a LINES=($(...)) 才能做到这一点。但即便如此,bash 也会在任何空格上拆分数组元素,而不仅仅是换行符。如果您要将进程替换用引号括起来,例如 LINES=("$(...)"),您将只有一个元素包含命令的整个输出。

    您可以使用read 循环进行必要的文本操作,以保留包含空格的元素数量。

    declare -a lines
    while IFS=, read -r line_number answer _ customer _; do
        if [[ $answer == @(yes|YES) ]]; then
            lines+=("Line number $line_number customer $customer")
        fi
    done < file.csv
    

    如 cmets 中所述,根据bash 版本,在[[ ... ]] 中使用@(...) 可能需要shopt -s extglob

    或者,if 可以替换为 case

    case $answer in
        yes|YES)
            LINES+=("Line number $line_number customer $customer")
            ;;          
    esac
    

    【讨论】:

    • 小提示:一些旧版本的bash 可能需要shopt -s extglob 来支持@(yes|YES) 模式。我总是忘记默认情况下哪个版本在[[ ... ]] 中启用了该选项。
    • 我对 csv 文件有疑问,csv 文件可能有四个以上的字段,例如 csv 中的行可能是 12 个字段,如 ( 1,yes,yes,customer1 ,a,b,c, d,e,f,r)
    • @maihabunash 这很容易解决。您可以添加另一个名为 _ 的垃圾 read 变量来处理尾随字段。我在最近的编辑中添加了额外的 _
    • @maihabunash 其余字段由_read -r line_number answer _ customer _ 块中捕获。要管理此信息,您可以将它们命名为 read -r line_number answer _ customer myfield _ 等名称。否则它们将被存储在 _ 中而无效。
    • 或者你可以将整行读入一个数组,然后挑选你想要的字段。
    【解决方案2】:

    试试这个:

    a=$(awk -F, 'BEGIN{IGNORECASE=1} $2=="yes"  {printf  "Line number %d customer %s;", $1, $4}' file.csv)    
    IFS=';' read -a LINES <<< "${a}"
    

    【讨论】:

    • 用一个额外的变量和一个分号作为行终止符和一个单独的读取命令使解决方案复杂化是没有用的。只需将 IFS 设置为换行符并使用数组分隔符填充 LINES 即可完成,请参阅我刚刚发布的解决方案。
    【解决方案3】:

    正如@JohnB 提到的,您将 LINES 填充为标量变量,而不是数组。试试这个:

    $ IFS=$'\n' LINES=( $(awk 'BEGIN{for(i=1;i<=3;i++) printf "\"Line number %d\"\n", i}') )
    
    $ echo ${#LINES[*]}
    3
    
    $ echo "${LINES[0]}"
    "Line number 1"
    
    $ echo "${LINES[1]}"
    "Line number 2"
    
    $ echo "${LINES[2]}"
    "Line number 3"
    

    并根据您的实际输入/输出进行调整,这可能会导致:

    IFS=$'\n' LINES=( $(awk -F, 'BEGIN{IGNORECASE=1} $2=="yes"{printf "\"Line number %d customer %s\"\n", $1, $4}' file.csv) )
    

    【讨论】:

    • @maihabunash 如果您同意这是正确的答案,您应该点击它旁边的复选标记,让其他人知道谁在将来查找此问题。
    【解决方案4】:

    如果你使用 bash,你可以使用 mapfile 内置:

    $ mapfile -t LINES < \
        <(awk -F, 'BEGIN{IGNORECASE=1} 
        $2=="yes"  {printf "\"Line number %d customer %s\"\n", $1, $4}' file.csv)
    $ echo "${#LINES[*]}"
    4
    $ echo "${LINES[@]}"
    "Line number 1 customer customer1" "Line number 3 customer customer11" "Line number 5 customer customer15" "Line number 6 customer customer21"
    

    【讨论】:

      猜你喜欢
      • 2014-06-28
      • 1970-01-01
      • 2012-07-11
      • 2022-01-24
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 2017-01-14
      • 2019-08-08
      相关资源
      最近更新 更多