如何使用shell脚本从具有多个类的java文件中检测主类名?

如何使用shell脚本从具有多个类的java文件中检测主类名?

我制作一个脚本java文件连接器使用catsed,连接多个.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 ClassNameclass 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 classclass,主类仍将剩下一个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搜索最后一次出现的,并追加另一个。将修改后的缓冲区传输回保留空间publicpublich
  • $!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这样做sedsed -e 's/.../.../' *.java与 相同cat *.java | sed -e 's/.../.../',但是,您只sed在第一个解决方案中运行,而不是cat-sed进程越少越好。

相关内容