如何在通配符输入上写入新文件名?

如何在通配符输入上写入新文件名?

我在科学实习中获得了一些使用 Inix 终端的经验,主要使用一些实用程序,例如grep, awksed但是有一件事我已经尝试弄清楚一段时间了,这确实会让我更有效率与我必须做的数字运算。

我有一个脚本run.awk,可以对大量文本文件执行一些操作。事实上,它将获取该文件chloride.out,从中提取数据并写入chloride.cm

无论如何,我可以让这个脚本根据 shell 中的初始通配符短语接收*.out并写入文件吗?*.cm

我为处理大量数据而编写的脚本数量已经超过一百次,这真是烦人。

理想情况下,我想知道是否有一种方法可以通过 shell 为我的所有脚本执行此操作。如果它不能在 shell 或等效工具中自动化,我是否可以至少awk以与我所描述的类似的方式自动化我的脚本?

答案1

您当然可以让 awk 通过通配符处理多个文件。一个建议是将 保留run.awk为通用“函数”,它接受单个文件并生成单个输出文件,然后从另一个脚本调用它,然后该脚本可以负责同化输入和输出文件。

例子

这将是一个 Bash 脚本,我们可以将其称为awk_runner.bash.

#!/bin/bash

for ifname in *.out; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done

样品运行

我创建了一个示例目录,其中包含一些测试文件。

$ touch file{1..4}.out

这导致生成了 4 个文件:

$ ls -1
file1.out
file2.out
file3.out
file4.out

现在我们运行我们的脚本:

$ ./awk_runner.bash
IN: file1.out, OUT: file1.cm
running run.awk with file1.out & file1.cm

IN: file2.out, OUT: file2.cm
running run.awk with file2.out & file2.cm

IN: file3.out, OUT: file3.cm
running run.awk with file3.out & file3.cm

IN: file4.out, OUT: file4.cm
running run.awk with file4.out & file4.cm

在以“running...”开头的每一行之后,我们的脚本可以从这里运行。

列表中的文件

假设我们不使用通配符,*.out而是使用一个包含文件名列表的文件,例如:

$ cat filelist.txt 
file1.out
file2.out
file3.out
file4.out

我们可以使用脚本的修改版本,它将使用循环while而不是for循环。现在我们将这个脚本的变体称为awk_file_runner.bash

#!/bin/bash

while read ifname; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done < filelist.txt

此版本的脚本从文件读取输入filelist.txt

done < filelist.txt

然后,对于循环的每一轮while,我们使用read命令从输入文件中读取一行。

while read ifname; do

然后,它以与第一个脚本相同的方式执行所有操作,其中它将在循环遍历文件的每一行时运行awk脚本。run.awk

答案2

您可以直接在 awk 中执行此操作,而不是编写 shell 包装器并为您处理的每个文件生成一个新的 awk 实例。如果您已有 awk 脚本,则可以使用 FILENAME 变量访问当前文件。因此,如果您运行awk 'some commands' file1 file2,您可以使用 FILENAME 判断您正在使用 file1 还是 file2。您还可以在 awk 中使用>on print/ 。printf所以,如果你有一个像这样的 awk 脚本

/pattern/{ print $1,$3 }

你可以轻松做到

/pattern/{ print $1,$3 > FILENAME".processed" }

或者用来FNR=1告诉您何时位于新文件中,并创建一个变量来对文件名进行更复杂的操作。就像用 替换.in扩展名一样.out,如

sauer@humpy:/tmp$ grep . file*.in
file1.in:a
file1.in:b
file2.in:c
sauer@humpy:/tmp$ awk 'FNR=1{out=FILENAME;sub("\.in$",".out",out)} {print "processed"$0 > out}' file*.in
sauer@humpy:/tmp$ grep . file*.out
file1.out:processeda
file1.out:processedb
file2.out:processedc

我用来grep .在此处显示文件名和多个文件的内容,这也是一个有趣的技巧。但重要的是,将变量的值设置为当更改为 1 时out的修改版本(因此我们位于文件的第 1 行),然后将所有打印重定向到.请注意,这有点危险,因为扩展名不匹配将导致无法替换,从而导致覆盖输入文件。因此,最好添加一个故障安全检查来确保这一点或类似的事情。这留给读者作为练习。 ;)FILENAMEFNRoutout != FILENAME

如果您需要一个包含文件名列表的文件,最简单的方法是像这样运行它

awkscript $(< /path/to/filename_list_file )

它获取 的内容filename_list_file并将其放在命令行上。

相关内容