繁体   English   中英

将文本文件的行放入bash中的数组中

[英]Put lines of a text file in an array in bash

我正在从一位同事那里接手一个 bash 脚本,该脚本读取一个文件,处理它并基于当前while循环中的行打印另一个文件。

我现在需要为其添加一些功能。 我现在遇到的问题是读取一个文件并将每一行放入一个数组中,除了该行的第二列可以为空,例如:

对于以\\t作为分隔符的文本文件:

A\tB\tC
A\t\tC

对于相同但以,作为分隔符的 CSV 文件:

A,B,C
A,,C

然后应该给

["A","B","C"] or ["A", "", "C"]

我接手的代码如下:

while IFS=$'\t\r' read -r -a col; do
# Process the array, put that into a file
lp -d $printer $file_to_print
done < $input_file

如果 B 被填充,这会起作用,但 B 现在有时需要为空,因此当输入文件将其保持为空时,创建的数组以及要打印的输出文件只会跳过这个空单元格(然后数组为["A","C"] )。

我尝试在 awk 上编写整个块,但这带来了它自己的一系列问题,使得调用 lp 命令进行打印变得困难。

所以我的问题是,如何将行中的空单元格保留到我的 bash 数组中,以便稍后调用它并使用它?

非常感谢你。 我知道这可能会很困惑,所以请询问,我会指定。

编辑:请求后,这是我尝试过的 awk 代码。 这里的问题是它只打印最后一个打印请求,而我知道它循环遍历整个文件,并且 lp 命令仍在循环中。

awk 'BEGIN {
    inputfile="'"${optfile}"'"
    outputfile="'"${file_loc}"'"
    printer="'"${printer}"'"
    while (getline < inputfile){
      print "'"${prefix}"'" > outputfile
      split($0,ft,"'"${IFSseps}"'");
      if (length(ft[2]) == 0){
        print "CODEPAGE 1252\nTEXT 465,191,\"ROMAN.TTF\",180,7,7,\""ft[1]"\"" >> outputfile
        size_changer = 0
      } else {
        print "CODEPAGE 1252\nTEXT 465,191,\"ROMAN.TTF\",180,7,7,\""ft[1]"_"ft[2]"\"" >> outputfile
        size_changer = 1
      }
      if ( split($0,ft,"'"${IFSseps}"'") > 6)
        maxcounter = 6;
      else
        maxcounter = split($0,ft,"'"${IFSseps}"'");
      for (i = 3; i <= maxcounter; i++){
        x=191-(i-2)*33
        print "CODEPAGE 1252\nTEXT 465,"x",\"ROMAN.TTF\",180,7,7,\""ft[i]"\"" >> outputfile
      }
      print "PRINT ""'"${copies}"'"",1" >> outputfile
      close(outputfile)
      "'"`lp -d ${printer} ${file_loc}`"'"
    }
    close("'"${file_loc}"'");
  }'

EDIT2:继续尝试找到解决方案,我尝试了以下代码但没有成功。 这很奇怪,因为只执行 printf 而不将其放入数组可以保持格式不变。

$ cat testinput | tr '\t' '>'
A>B>C
A>>C

# Should normally be empty on the second ouput line
$ while read line; do IFS=$'\t' read -ra col < <(printf "$line"); echo ${col[1]}; done < testinput
B
C

对于选项卡,它很复杂。

从手册中的3.5.7 分词

一系列 IFS 空白字符也被视为分隔符。

由于制表符是“IFS 空白字符”,制表符序列被视为单个分隔符

IFS=$'\t' read -ra ary <<<$'A\t\tC'
declare -p ary
declare -a ary=([0]="A" [1]="C")

您可以做的是将制表符转换为非空白字符,假设它不与字段中的实际数据冲突:

line=$'A\t\tC'
IFS=, read -ra ary <<<"${line//$'\t'/,}"
declare -p ary
declare -a ary=([0]="A" [1]="" [2]="C")

为了避免数据中逗号冲突的风险,我们可以使用一个不常见的ASCII字符:FS,八进制034

line=$'A\t\tC'
printf -v FS '\034'
IFS="$FS" read -ra ary <<<"${line//$'\t'/"$FS"}"

# or, without the placeholder variable
IFS=$'\034' read -ra ary <<<"${line//$'\t'/$'\034'}"

declare -p ary
declare -a ary=([0]="A" [1]="" [2]="C")

它可能只是您的# Process the array, put that into a file部分。

IFS=, read -ra ray <<< "A,,C"
for e in "${ray[@]}"; do o="$o\"$e\","; done
echo "[${o%,}]"
["A","","C"]

请参阅@Glenn 关于选项卡的出色回答。

我的简单数据文件:

$: cat x # tab delimited, empty field 2 of line 2
a   b   c
d       f

我的测试:

while IFS=$'\001' read -r a b c; do
  echo "a:[$a] b:[$b] c:[$c]"
done < <(tr "\t" "\001"<x)
a:[a] b:[b] c:[c]
a:[d] b:[] c:[f]

请注意,我使用了 ^A(一个 001 字节),但您可以使用像逗号或管道 ( | ) 字符这样简单的东西。 根据您的数据进行选择。

一个使用参数扩展的bash示例,我们将分隔符转换为\\n并让mapfile在每一行中作为新的数组条目读取...

对于制表符分隔的数据:

for line in $'A\tB\tC' $'A\t\tC'
do
    mapfile -t array <<< "${line//$'\t'/$'\n'}"
    echo "############# ${line}"
    typeset -p array
done

############# A B       C
declare -a array=([0]="A" [1]="B" [2]="C")
############# A         C
declare -a array=([0]="A" [1]="" [2]="C")

注意: $'...'构造确保\\t被视为单个<tab>字符,而不是两个文字字符\\ + t

对于逗号分隔的数据:

for line in 'A,B,C' 'A,,C'
do
    mapfile -t array <<< "${line//,/$'\n'}"
    echo "############# ${line}"
    typeset -p array
done

############# A,B,C
declare -a array=([0]="A" [1]="B" [2]="C")
############# A,,C
declare -a array=([0]="A" [1]="" [2]="C")

注意:这显然 (?) 假定所需的数据不包含逗号 ( , )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM