【问题标题】:printing contents of variable to a specified line in outputfile with sed/awk使用 sed/awk 将变量的内容打印到输出文件中的特定行
【发布时间】:2018-08-29 13:09:45
【问题描述】:

我一直在编写一个脚本,将多个 csv 文件连接成一个大的 csv。 csv 包含文件夹的名称及其各自的大小,采用 2 列设置,格式为“大小,项目名称”

单个 csv 文件示例:

49747851728,ODIN
32872934580,_WORK
9721820722,LIBRARY
4855839655,BASELIGHT
1035732096,ARCHIVE
907756578,USERS
123685100,ENV
3682821,SHOTGUN
1879186,SALT
361558,SOFTWARE
486,VFX
128,DNA

对于我当前的测试,我有 25 个类似的文件,第一列中的数字不同。

我正在尝试让此脚本执行以下操作:

  • 读取每个 csv 文件
  • 对于它看到的每个项目,如果该项目已打印到文件中,请扫描输出文件。如果没有,请打印项目名称
  • 对于每个文件,对于每个项目,如果找到项目,则将大小打印到输出 csv。

但是,我需要所有项目都在文本行 1 上,以逗号分隔,因此我可以将此输出文件用作 javascript 图形的输入。尺寸应添加到其项目名称下方的列中。

我当前的脚本:

csv_folder=$(echo "$1" | sed 's/^[ \t]*//;s/\/[ \t]*$//')
csv_allfiles="$csv_folder/*.csv"
csv_outputfile=$csv_folder.csv
echo -n "" > $csv_outputfile

for csv_inputfile in $csv_allfiles; do
  while read line && [[ $line != "" ]]; do
    projectname=$(echo $line | sed 's/^\([^,]*\),//')
    projectfound1=$(cat $csv_outputfile | grep -w $projectname)
if [[ ! $projectfound1 ]]; then
  textline=1
  sed "${textline}s/$/${projectname}, /" >> $csv_outputfile
    for csv_foundfile in $csv_allfiles; do
    textline=$(echo $textline + 1 | bc )
    projectfound2=$(cat $csv_foundfile | grep -w $projectname)
    projectdata=$(echo $projectfound2 | sed 's/\,.*$//')
        if [[ $projectfound2 ]]; then
          sed "${textline}s/$/$projectdata, /" >> $csv_outputfile
        fi
      done
    fi
  done < $csv_inputfile
done

我当前的脚本找到了正确的信息(项目名称、项目数据),如果我只是“回显”这些变量,它会将正确的数据打印到文件中。但是,使用 echo 它只会在每个项目中打印出很长的列表。我希望它“跳回”到第 1 行并在当前行的末尾打印新项目,然后运行循环以在每个下一行的末尾打印数据。

我认为这应该可以通过 sed 或 awk 实现。 sed 应该有一种使用

将文本插入特定行的方法
sed '{n}s/search/replace/'

其中 {n} 是要插入的行

awk 应该能够用类似

的东西做同样的事情
awk -v l2="$textline" -v d="$projectdata" 'NR == l2 {print d} {print}' >> $csv_outputfile

但是,将脚本中的 sed 命令替换为

echo $projectname 
echo $projectdata 

吐出正确的信息(所以我知道我的变量填写正确) sed 和 awk 命令倾向于吐出它们当前 inputcsv 的全部内容;不只是我希望他们这样做。

Pastebin 输出每个写入文件的变体

如您所见,sed 输出倾向于粘贴 inputcsv 的全部内容,使循环在一次迭代后停止。 (因为它会在一个循环后找到其他项目)

所以我的问题就是其中之一;

  • 如何使 sed / awk 以我希望的方式运行;即仅将我的 var 中的信息打印到当前文本行,而不是整个输入 csv。 sed 能做到这一点,只打印一行变量吗?或者
  • 我是否应该通过“echo”将变量输出到一个临时文件中,然后遍历该临时文件以使 sed 按我希望的方式对行进行排序? (请记住,将来会添加更多 .csv 文件,我不能让它循环 x 次来对信息进行排序)
  • 有没有一种方法可以在不使用 sed 或 awk 的情况下将文本回显/打印到特定的文本行?是否有我缺少的 printf 选项?其他想法?

非常感谢任何帮助。

【问题讨论】:

  • 这是一个 X-Y 问题。您正在尝试解决由您的解决方案选择引起的问题。除了连接文件之外,我还不清楚您要做什么?一个项目是否在多个文件中?如果是这样,您是否将尺寸相加?或者,选择第一个?给定输入文件的预期输出是什么?
  • 无论你想做什么,用一个小的 awk 脚本来解决都非常简单,但我们无法判断你想做什么,因为你没有提供清楚的说明,简洁,可测试的样本输入和预期输出。只需这样做,您就会得到答案。
  • 我真的不知道我的请求不清楚。我的帖子顶部附近给出了一个示例输入。我有很多 csv 文件,其中包含一个包含“大小,文件夹名称”格式的文件夹大小的列表。我希望这些文件夹以列的形式显示,它们的大小在它们下方,我的文件夹中的每个 .csv 文件占一行。期望的输出:pastebin.com/5wiuq53n 我也会将该 pastebin 添加到我的原始帖子中。

标签: bash csv awk sed


【解决方案1】:

如果您对按名称排序的输出感到满意,则此单行可能有用:

awk 'BEGIN {FS=",";OFS=","} {print $2,$1}' * | sort | uniq

文件必须位于同一目录中。如果不是文件列表,则替换 *.首先它交换两个字段。 awk 将获取文件列表并进行连接。然后对行进行排序并仅打印唯一的行。这取决于项目大小始终相同。

上面简单的一行代码为每个项目提供了一行代码。如果您真的想在 awk 中完成所有操作并使用 awk 编写这两行代码,则需要执行以下操作。最后还有第二个 awk,它将每个列条目累积在一个数组中,然后在最后将其吐出:

awk 'BEGIN {FS=","} {print $2,$1}' *| sort |uniq | awk 'BEGIN {n=0}
{p[n]=$1;s[n++]=$2}
END {for (i=0;i<n;i++) printf "%s,",p[i];print "";
for (i=0;i<n;i++) printf "%s,",s[i];print ""}'

如果你有 rs 实用程序,那么这可以简化为

awk 'BEGIN {FS=","} {print $2,$1}' *| sort |uniq | rs -C',' -T

【讨论】:

    【解决方案2】:

    用项目名称填充数组,用值填充数组,然后用 bash printf 打印它们,您可以在 printf 命令中选择列宽(当前为 13 个字符 - %13s)

    #!/bin/bash
    declare -i index=0
    declare -i pindex=0
    while read project; do
      parray[$pindex]=$project
      index=0
      while read;do
        array[$pindex,$index]="$REPLY"
        index+=1
      done <<< $(grep -h "$project" *.csv|cut -d, -f1)
      pindex+=1
    done <<< $(cat *.csv|cut -d, -f 2|sort -u)
    maxi=$index
    maxp=$pindex
    
    for (( pindex=0; $pindex < $maxp ; pindex+=1 ));do
     STR="%13s $STR"
     VAL="$VAL ${parray[$pindex]}"
    done
    printf "$STR\n" $VAL
    for (( index=0; $index < $maxi;index+=1 ));do
      STR=""; VAL=""
      for (( pindex=0; $pindex < $maxp;pindex+=1 )); do
        STR="%13s $STR"
        VAL="$VAL ${array[$pindex,$index]}"
      done
      printf "$STR\n" $VAL
    done
    

    【讨论】:

      【解决方案3】:

      完成这种转置的一种方法是将数据保存到关联数组中。

      在以下示例中,我们使用二维数组来跟踪我们的数据。因为排序似乎很重要,所以我们创建一个 col 数组并在看到新的 projectname 时创建一个新的增量——这个 col 数组最终成为我们数据的第一个索引。我们还创建了一个行数组,每当我们看到当前列的新数据时,我们都会递增该数组。行号是我们对数据的第二个索引。最后,我们打印出所有记录。

      #! /usr/bin/awk -f
      BEGIN {
          FS  = ","
          OFS = ", "
          rows=0
          cols=0
          head=""
          split("", data)
          split("", row)
          split("", col)
      }
      !($2 in col) { # new project
          if (head == "")
              head = $2
          else
              head = head OFS $2
          i = col[$2] = cols++
          row[i] = 0
      }
      {
          i = col[$2]
          j = row[i]++
          data[i,j] = $1
          if (j > rows)
              rows = j
      }
      END {
          print head
          for (j=0; j<=rows; ++j) {
              if ((0,j) in data)
                  x = data[0,j]
              else
                  x = ""
              for (i=1; i<cols; ++i) {
                  if ((i,j) in data)
                      x = x OFS data[i,j]
                  else
                      x = x OFS
              }
              print x
          }
      }
      

      作为奖励,这里有一个脚本可以从您的一个粘贴箱中重现详细输出。

      #! /usr/bin/awk -f
      BEGIN {
          FS = ","
          split("", data) # accumulated data for a project
          split("", line) # keep track of textline for data
          split("", idx)  # index into above to maintain input order
          sz = 0
      }
      $2 in idx { # have seen this projectname
          i = idx[$2]
          x =   ORS "textline = " ++line[i]
          x = x ORS "textdata = " $1
          data[i] = data[i] x
          next
      }
      { # new projectname
          i = sz++
          idx[$2] = i
          x =       "textline = 1"
          x = x ORS "projectname = " $2
          x = x ORS "textline = 2"
          x = x ORS "projectdata = " $1
          data[i] = x
          line[i] = 2
      }
      END {
          for (i=0; i<sz; ++i)
              print data[i]
      } 
      

      【讨论】:

      • 你在“关联数组”中找到了它。
      猜你喜欢
      • 2019-03-29
      • 2015-10-28
      • 2014-02-24
      • 1970-01-01
      • 1970-01-01
      • 2013-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多