我在科学实习中获得了一些使用 Inix 终端的经验,主要使用一些实用程序,例如grep
, awk
,sed
但是有一件事我已经尝试弄清楚一段时间了,这确实会让我更有效率与我必须做的数字运算。
我有一个脚本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 行),然后将所有打印重定向到.请注意,这有点危险,因为扩展名不匹配将导致无法替换,从而导致覆盖输入文件。因此,最好添加一个故障安全检查来确保这一点或类似的事情。这留给读者作为练习。 ;)FILENAME
FNR
out
out != FILENAME
如果您需要一个包含文件名列表的文件,最简单的方法是像这样运行它
awkscript $(< /path/to/filename_list_file )
它获取 的内容filename_list_file
并将其放在命令行上。