使用 awk 迭代几千个文件,并在一次或两次运行中写入相同的文件



我在他们自己的目录中有很多文件。它们都具有相同的名称结构:

2019-10-18-42-IV-Friday.md
2019-10-18-42-IV-Saturday.md
2019-10-18-42-IV-Sunday.md
2019-10-18-43-43-IV-Monday.md
2019-10-18-42-IV Tuesday.md

等等。

这是详细的: 年-月-日-日-日-周 年-实际季度 week.md

我想将一行作为第二行写入每个文件: 使用 awk,我想从文件名中提取并扩展日期,然后将它们写入相应的文件。

这就是我失败的地方。

%!awk -F"-"-" '{print "Today is $6 ", the " $3"."$2"."$1", Kw "$4", in the" $5 ". Quarter."}'

这很好用,我得到了我想写进文件的句子。

所以把整个事情放在一个循环中:

ze.sh  
#!/bin/bash                                                                 
for i in *.md;                                                              
j = awk -F " " '{ print "** Today is " $6 ", the" $3"." $2"." $1", Kw " $4 ", in the " $5 ". Quarter. **"}' $i 
Something with CAT, I suppose.                                                             
end 

我该怎么做才能使变量i遍历所有文件,从$i中提取j的值,然后将$j写入每个文件的第二行?

非常感谢您的帮助。

[使用 manjaro linux 和 bash] GNU bash, Version 5.0.11(1)-release (x86_64-pc-linux-gnu) Linux 版本 5.2.21-1-MANJARO

你能尝试以下吗(还没有测试过,GNUawk是需要的)。为了在第二行写日期,我选择了与您的Input_file具有日期相同的格式。

awk -i inplace '
FNR==2{
split(FILENAME,array,"-")
print array[1]"-"array[2]"-"array[3]
}
1
' *.md

如果可能,请先尝试不使用-i inplace选项,以便更改不会保存到Input_file中,一旦您对结果感到满意,就可以将其添加到代码中,以将就地更改为Input_file。

有关就地更新支持的awk版本,请参阅James sir发布的链接。

使用 awk 就地保存修改

对于就地更新文件,sed比 awk 更适合,因为:

  • 您不需要最新版本,旧版本也可以
  • 可以在GNU和BSD风格中工作 ->更便携

但首先,要将文件名拆分为其部分,您不需要额外的过程,内置的read也可以做到这一点。从您的示例中,我们需要提取年、月、日、周数字、四分之一字符串和工作日名称字符串:

2019-10-18-42-IV-Friday.md
2019-10-18-42-IV-Saturday.md
2019-10-18-42-IV-Sunday.md
2019-10-18-43-43-IV-Monday.md
2019-10-18-42-IV Tuesday.md

对于前 3 行,这个简单的表达式将起作用:

IFS=-. read year month day week q dayname rest <<< "$filename"

最后一行在工作日名称之前有一个空格,而不是-,但这很容易修复:

IFS='-. ' read year month day week q dayname rest <<< "$filename"

第 4 行更难修复,因为它具有不同数量的字段。为了处理额外的字段,我们应该添加一个额外的变量项:

IFS='-. ' read year month day week q dayname ext rest <<< "$filename"

然后,如果我们可以假设可以忽略该行上的第二个43并且我们可以移动参数,那么我们使用$ext值的条件。 也就是说,对于大多数行,ext的值将md(文件扩展名)。 如果值不同,则意味着我们有一个额外的字段,我们应该移动值:

if [[ $ext != "md" ]; then
q=$dayname
dayname=$ext
fi

现在,我们可以使用变量来格式化要插入到文件中的行:

line="Today is $dayname, the $day.$month.$year, Kw $week, in the $q. Quarter."

最后,我们可以制定一个sed语句,例如在第一个语句之后附加我们的自定义格式化行,理想情况下,可以同时使用 GNU 和 BSD 风格的sed

这将等效地适用于GNU和BSD版本:

sed -i.bak -e "1 a\"$'n'"$line"$'n' "$filename" && rm *.bak

请注意,创建的备份文件.bak必须手动删除。

如果你不想创建备份文件,那么恐怕你需要对GNU和BSD风格使用稍微不同的格式:

# GNU
sed -i'' -e "1 a\"$'n'"$line"$'n' "$filename"
# BSD
sed -i '' -e "1 a\"$'n'"$line"$'n' "$filename"

事实上,如果你只需要支持GNU风格,那么更简单的形式也可以工作:

sed -i'' "1 a$line" "$filename"

您可以将所有这些放在一个for filename in *.md; do ...; done循环中。

您可能希望将文件名输入到 AWK 脚本中,使用"-"分隔组件。

此脚本假定第二行需要将 AWK 输出附加到文件中:

for i in *.md ; do
echo $i | awk -F- 'AWK COMMAND HERE' >> $i
done

如果必须将新文本(作为第二行)插入到新文件中,则可以使用sed程序执行更新文件(使用就地编辑 '-i')。类似的东西

for i in *.md ; do
mark=$(echo $i | awk -F- 'AWK COMMAND HERE')
sed -i -e "2i$mark" $i
done

这对我来说是最好的解决方案,特别是因为它可以应对不同的分隔符。

非常感谢所有对这个问题感兴趣的人,尤其是那些发布解决方案的人。

我希望我没有因为错误输入示例数据而变得如此困难。

这现在是解决方案的"我的"变体:

for filename in *.md; do 
IFS='-. ' read year month day week q dayname rest <<< "$filename"
line="Today is $dayname, the $day.$month.$year, Kw $week, in the $q. Quarter."
sed -i.bak -e "1 a\"$'n'"$line"$'n' "$filename" && rm *.bak;
done

由于有多个字段分隔符,因此最好使用结果。

但也许我错了,其他解决方案也提供了使用不同分隔符的可能性:至少需要"-"和"."。

作为一名新人,我很快就收到了非常好的答案,我感到非常惊讶和高兴。希望我能回馈一些东西。

我也惊讶于对于出现的问题有多少种不同的解决方案。

如果有人对我所做的事情感兴趣,请继续阅读: 我得了两年致命的自身免疫性疾病。渐渐地,我的大脑被摧毁了,断断续续。

尤其是我的记忆力遭受了很多痛苦;我经常不记得我昨天做了什么,知道还有什么需要做。

这就是为什么我创建了直到 2030 年 12 月 31 日的日文件,每天都有一个降价模板。然后,我在那里记录我在那些日子里所做的事情和学到的东西,以及仍然需要做的事情。

对我来说,在单个文件中确定正确的日期很重要。为什么没有数据库,为什么要降价?

我希望拥有一种可以在任何地方、任何设备和任何操作系统上使用的格式。一种不属于公司的格式,可以改变它或使其更昂贵,可以将其从市场上移除或用许可证限制它。

它足够快。如上所述,对 4,097 个文件的更改在我的 i2 笔记本电脑(12 GB RAM,SSD)上花费了不到 5 秒的时间。

使用 fzf 搜索所有文件也非常快。我可以简单地转换文件并输出为我只需要的东西。

我的记忆不会从中恢复过来,但我有机会记录我忘记的东西。

非常感谢您的帮助和关注。

最新更新