使用 sed/awk 将变量的内容打印到输出文件中的指定行

Jas*_*onD 5 csv bash awk sed

我一直在编写一个脚本来将多个 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
Run Code Online (Sandbox Code Playgroud)

对于我当前的测试,我有 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
Run Code Online (Sandbox Code Playgroud)

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

我想这应该可以用 sed 或 awk。sed 应该有一种将文本插入到特定行的方法

sed '{n}s/search/replace/'
Run Code Online (Sandbox Code Playgroud)

其中 {n} 是要插入的行

awk 应该能够用类似的东西做同样的事情

awk -v l2="$textline" -v d="$projectdata" 'NR == l2 {print d} {print}' >> $csv_outputfile
Run Code Online (Sandbox Code Playgroud)

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

echo $projectname 
echo $projectdata 
Run Code Online (Sandbox Code Playgroud)

吐出正确的信息(所以我知道我的变量被正确填充)sed 和 awk 命令倾向于吐出它们当前 inputcsv 的全部内容;不仅仅是我想让他们做的那条线。

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

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

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

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

任何帮助将不胜感激。

Mic*_*ack 1

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

在下面的示例中,我们使用二维数组来跟踪数据。因为排序似乎很重要,所以我们创建一个 col 数组,并在看到新项目名称时创建一个新的增量——这个 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
    }
}
Run Code Online (Sandbox Code Playgroud)

作为奖励,这里有一个脚本可以重现您的一个 Pastebin 的详细输出。

#! /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]
} 
Run Code Online (Sandbox Code Playgroud)