我制作一个脚本java文件连接器使用cat
和sed
,连接多个.java
文件:
#!/bin/bash
########################################################################################################################
# This file create a "MainFileName.java.output" file concatenating all java file in this directory.
# Run this script as '$ ./javaFilesConcatenator MainFileName' when "MainFileName.java" contains the main method.
########################################################################################################################
cat *.java | sed -e 's/public class/class/g;s/public interface/interface/g' >$1.java.output
sed -i "s/class $1/public class $1/g" $1.java.output
为了运行这个脚本,我将主类名称作为用户的输入参数,因为主类应该是public
,而其他类不是。我将所有内容替换public class ClassName
为class ClassName
除主类之外的内容。
现在,我想修改我的脚本,使其对用户更友好,以便它在不提供主类名称的情况下检测主类。我想这是可能的,因为主类与其他类不同。它包含一个main
方法。
并且,另一个要求是它必须通过从输出文件的不同位置(每个文件在开头都有一些语句,连接这些语句后并不全部在开头)进行剪切,将所有import
(ig ) 语句放在文档的开头。import java.util.Scanner;
import
另一个可选要求是它删除所有package com.alhelal.texpad;
类似的行,但不删除包含 word 的注释package
。
怎么样,我能满足我的要求吗?
答案1
该sed
脚本应该执行所有步骤,但我无法测试它,因为我没有测试数据:
sed '/^[^/]*package/d
/import *java\./{p;d;}
H;/void main()/{g;s/.*\n[[:space:]]*public/& public/;h;}
$!d;g;s/public class/class/g' *.java
该脚本的主要概念:
- 您可以将多个文件名传递给
sed
;它们被逐行地阅读。sed
将不知道一个文件和另一个文件何时启动,但无论如何我们在这里不需要它。 - 由于
sed
逐行工作,我们需要一些内存来存储稍后需要的行。这是重新排列文件所必需的(所有导入都位于输出顶部)。在 中sed
,该内存是保持空间;把它想象成一个剪贴板。虽然模式空间在脚本的每个循环中都会被新行覆盖,但保持缓冲区保持不变。您可以使用 将模式空间复制到保留空间,h
或者使用 复制回g
或交换两者x
。将H
模式空间的内容附加到保留空间(中间有换行符),反之亦然g
。因此,我们想要在输出顶部的行会立即打印(使用p
),而所有其他行都将附加到保留空间。当我们到达所有行的末尾后,就可以处理保留空间中收集的行了。 - 除了一个之外,所有的都
public
应该被删除。我用一个技巧来做到这一点:只要我遇到该main
方法,我就知道最后一个类是保持公开的类,因此我需要将其标记为公共类。但如何呢?窍门:我public
在那里添加了第二个,所以我们得到public class fooClass ... public public class thisIsTheMainClass ... public class barClass
,所以如果我们稍后将所有替换public class
为class
,主类仍将剩下一个public
属性。第二个的添加public
发生在第三行(我们需要将保留空间复制到模式空间以使用它,然后将其复制回来)。 - 要记住的一件事:您可以在
sed
命令前面加上“地址”,因此它们仅针对与该地址匹配的行执行。地址可以是包含在斜线中的正则表达式模式以匹配行;这用在脚本的前三行中。或者它可以是行号或$
最后一行,例如脚本的第四行,其中反转!
所选地址,因此d
对除最后一行之外的所有行执行 elete。如果地址后面跟着 括起来的命令{}
,则内部的所有命令仅针对该地址执行,如脚本的第二行和第三行。
脚本详细内容:
/^[^/]*package/d
删除所有包含该单词package
并且之前没有斜杠的行。我希望这是你想要的/import *java\./{p;d;}
立即打印所有带有 java 导入的行并停止进一步处理。H
收集保留空间中的所有其他行而不是打印它们。这就是我们在输出开始时收集所有导入的方式/void main()/{g;s/.*\n[[:space:]]*public/& public/;h;}
:当满足该main
方法时,g
将迄今为止收集的行转移到模式空间,该命令在行的开头s
搜索最后一次出现的,并追加另一个。将修改后的缓冲区传输回保留空间public
public
h
$!d
如果这不是最后一行,则无需执行进一步操作g;s/public class/class/g'
将我们收集的所有非导入行移动到模式空间并public
从类中删除所有行。当我们在主类中添加第二个时public
,主类将保持公开状态
请注意,您通常可以使用某些输入(标识符或注释中的关键字)来中断这样的脚本。
答案2
首先要找到main方法,文件basename就是类名
CLASS=$(basename -s .java $(grep -l 'public static void main' *.java))
然后,提取所有导入,对它们进行排序,并使用uniq
.假设没有与其他导入相匹配的通配符导入。
sed -n '/^[ \t]*import /p' *.java | sort | uniq > $CLASS.java.output
然后处理 Java 文件,提取导入和包:
grep -hv '^[ \t]*import \|^[ \t]*package' *.java | sed -e 's/public class/class/g;s/public interface/interface/g' >>$CLASS.java.output
另一种方式,纯 sed:
sed -e '/^[ \t]*\(import\|package\)/d;s/public class/class/g;s/public interface/interface/g' .java >>$CLASS.java.output
然后你想恢复你的公开课
sed -i "s/class $CLASS/public class $CLASS/g" $CLASS.java.output
请注意,您不必在 shebang 中使用 bash (#!/bin/sh
已经足够好了),因为这里没有任何 bashism。
另请注意,您不必cat-pipe
这样做sed
,sed -e 's/.../.../' *.java
与 相同cat *.java | sed -e 's/.../.../'
,但是,您只sed
在第一个解决方案中运行,而不是cat
-sed
进程越少越好。