【问题标题】:flat file parsing in shell在 shell 中解析平面文件
【发布时间】:2026-01-26 09:10:02
【问题描述】:

我有一个固定长度的文件格式:

Name       Age        Party              Role 
---------- ---------- ------------------ --------------
Shubham    27         XYZ                User
Drek       28         ABC                Admin
Raj        23         USR                User

现在我想编写一个 shell 脚本/命令来输出一个包含所有年龄

Party
-----------------
USR

我是 awk 和 shell 的新手。我尝试使用 awk 和 substr,但它太贵了,因为我的文件很大(> 200000 行,多列)。有没有一种巧妙的方法来做到这一点?

更新

任何字段中都可以有空格。真正的想法是该文件是一个固定长度的文件。所以每条记录的长度是固定的(姓名:10,年龄:10,部分:20,角色:10)。但是,记录中可以包含任何内容,包括空格和空格。例如:

Name       Age        Party              Role 
---------- ---------- ------------------ --------------
Shub A     27         XYZ & A            User
Drek GH    28         ABC & C            Admin
Raj        23         USR                User

等等。 现在我想使用 Name 进行选择,这样我的脚本就会打印出 Name = "Shub A" 的 Party 记录。所以这里的输出应该是:

Party
-------------------
XYZ & A

【问题讨论】:

  • 200000 行几乎算不上“巨大”。
  • 关于您的更新,当字段或多或少固定时,awk 非常棒,如果所有字段都是动态的,它就会变得棘手。是否可以使文件成为 csv-one,即逗号分隔不同的列或类似的东西。那么这个问题就很容易解决了。
  • 我可能可以使用 sed 用逗号替换多个空格。这将是一个单独的问题,正如我所说的,我对 shell 很陌生。
  • 谢谢大家的回答。我使用了 cut 和 awk 的组合。

标签: parsing shell unix awk fixed


【解决方案1】:

试试:

gawk 'BEGIN{ FIELDWIDTHS = "11 11 19 14" } NR<3 || $1~/^Shub A +$/{print $3}' file

【讨论】:

    【解决方案2】:

    这样的事情应该可以工作。它打印前两行(标题),然后比较第二个字段是否低于 25。

    awk 'FNR < 3 || $2 < 25 { print $3 }' infile
    

    它产生:

    Party
    ------------------
    USR
    

    编辑:这是在更新之前发布的,不适用于它。看看其他答案

    【讨论】:

      【解决方案3】:
      $ awk '($2+0) < 25{print $3}' input
      Party
      ------------------
      USR
      

      更新

      各种for循环判断哪个字段包含数字(n),然后名字在$1..n,party-field在$n+1..NF-1

      /Shub A/ {
          # determine which field that contains a number
          for (i=1;i<NF;i++) {
              if ($i ~ /[0-9]+$/) {
                  break
              }
          }
          for (j=1;j<i;j++) {
              printf "%s ", $j
          }
          for (k=(i+1);k<NF;k++) {
              printf "%s ", $k
          }
      
      }
      

      输出:

      Shub A XYZ & A 
      

      ...或者您可以尝试拆分“2 个或更多空格”,即

      $ awk -F"  +" '/^Shub/{print $3}' input
      XYZ & A
      

      【讨论】:

      • 如果我想根据名称进行选择怎么办? awk '($1+0) == "Shubham"{print $3}' 不适合我?
      • 哦,好吧,它!如果名称有空格,这也不起作用,例如。如果名称是 Shubh A 。如何克服空间问题?
      • 你的意思是像这样awk '$1=="Shubham"{print $3}' input 如果你告诉 awk 分割空白(默认)然后字符串被修剪
      • 如果名称中有空格。事情变得有点棘手。更新您的问题,我们会看看!参数NF 包含字段数。 $NF 是最后一个,$NF-1 是倒数第二个,依此类推...
      【解决方案4】:

      如果你知道你的年龄没有一个能填满 10 位数字,你可以这样做:

      < input-file cut -b 11-30 | awk '$1 < 25' | cut -b 11-
      

      【讨论】:

        【解决方案5】:

        试试这个,如果它对你有用:

         awk 'NR<3||($2+0)<25{a[++i]=$3}END{for(x in a)print a[x]}' file
        

        【讨论】: