Reputation: 984
I want to write a Java method which reads in a bash script into a single String object, then break that String down into minimal array of String commands so that I can execute them one command at a time. The problem I'm having is that I need to keep the block statements (eg if and while and input redirects) intact, that is, not to break them down further.
As an example, the following bash script:
./configure --sbindir=/lib/security \
--docdir=/usr/share/doc/Linux-PAM-1.1.3 \
--enable-read-both-confs &&
make
install -v -m755 -d /etc/pam.d &&
cat > /etc/pam.d/other << "EOF"
auth required pam_deny.so
account required pam_deny.so
password required pam_deny.so
session required pam_deny.so
EOF
rm -rfv /etc/pam.d
make install &&
chmod -v 4755 /lib/security/unix_chkpwd &&
mv -v /lib/security/pam_tally /sbin &&
mv -v /lib/libpam{,c,_misc}.la /usr/lib &&
sed -i 's| /lib| /usr/lib|' /usr/lib/libpam_misc.la &&
if [ -L /lib/libpam.so ]; then
for LINK in libpam{,c,_misc}.so; do
ln -v -sf ../../lib/$(readlink /lib/${LINK}) /usr/lib/${LINK} &&
rm -v /lib/${LINK}
done
fi
echo done
I want to programmatically break it down to
./configure --sbindir=/lib/security --docdir=/usr/share/doc/Linux-PAM-1.1.3 --enable-read-both-confs
make
install -v -m755 -d /etc/pam.d
cat > /etc/pam.d/other << "EOF"
auth required pam_deny.so
account required pam_deny.so
password required pam_deny.so
session required pam_deny.so
EOF
rm -rfv /etc/pam.d
make install
chmod -v 4755 /lib/security/unix_chkpwd
mv -v /lib/security/pam_tally /sbin
mv -v /lib/libpam{,c,_misc}.la /usr/lib
sed -i 's| /lib| /usr/lib|' /usr/lib/libpam_misc.la
if [ -L /lib/libpam.so ]
then
for LINK in libpam{,c,_misc}.so; do
ln -v -sf ../../lib/$(readlink /lib/${LINK}) /usr/lib/${LINK} &&
rm -v /lib/${LINK}
done
fi
echo done
Can this be done using regular expression or by some other means using Java?
Even some pseudo code would be welcome.
Upvotes: 0
Views: 213
Reputation: 984
This is what I got so far. I have not tested it exhaustively.
String compoundCommand = null;
ArrayList<String> commandList = new ArrayList<String>();
String list = "";
int count = 0;
ArrayList<String> splitBashScript(String script) {
script = script.replaceAll("\\\\\n", "");
script = script.replaceAll("([^;]);([^;])", "$1\n$2");
String[] lines = Pattern.compile("[ \t]*\n", Pattern.MULTILINE).split(script);
String delimiter = null;
for (String line : lines) {
if (!line.isEmpty()) {
if (compoundCommand == null) {
if (line.matches(".*<<.*")) {
compoundCommand = "here";
delimiter = line.replaceFirst(".*<< *", "").replaceAll("\"", "");
list += line;
} else if (line.matches("[ \t]*for[ \t]*.*")) {
compoundCommand = "for";
count++;
list += line;
} else if (line.matches("[ \t]*select[ \t]*.*")) {
compoundCommand = "select";
count++;
list += line;
} else if (line.matches("[ \t]*case[ \t]*.*")) {
compoundCommand = "case";
count++;
list += line;
} else if (line.matches("[ \t]*if[ \t]*.*")) {
compoundCommand = "if";
count++;
list += line;
} else if (line.matches("[ \t]*while[ \t]*.*")) {
compoundCommand = "while";
count++;
list += line;
} else if (line.matches("[ \t]*until[ \t]*.*")) {
compoundCommand = "until";
count++;
list += line;
} else if (list.isEmpty()) {
commandList.add(line.replaceFirst("[ \t]*&&$", ""));
}
} else if (compoundCommand.equals("here")) {
list += "\n" + line;
if (line.matches(delimiter)) {
compoundCommand = null;
commandList.add(list.replaceFirst("[ \t]*&&$", ""));
list = "";
}
} else if (compoundCommand.equals("for")) {
compound(line, "(for|select|while|until)", "done");
} else if (compoundCommand.equals("select")) {
compound(line, "(for|select|while|until)", "done");
} else if (compoundCommand.equals("case")) {
compound(line, "case", "esac");
} else if (compoundCommand.equals("if")) {
compound(line, "if", "fi");
} else if (compoundCommand.equals("while")) {
compound(line, "(for|select|while|until)", "done");
} else if (compoundCommand.equals("until")) {
compound(line, "(for|select|while|until)", "done");
}
}
}
return commandList;
}
void compound(String line, String start, String end) {
list += "\n" + line;
if (line.matches("[ \t]*" + start + "[ \t]*.*")) {
count++;
}
if (line.matches("[ \t]*" + end + "[ \t]*.*")) {
count--;
}
if (count == 0) {
compoundCommand = null;
commandList.add(list.replaceFirst("[ \t]*&&$", ""));
list = "";
}
}
Upvotes: 0
Reputation: 27478
You really need a parser!
Have a look at this project jbash which sounds very similar to what you want to do.
They seem to have built all the basic components you will need!
Upvotes: 0
Reputation: 206831
Regular expressions are not enough for this, you would need to parse the syntax properly to get something that works reliably.
Parsing shell scripts is not easy at all (no formal grammar AFAIK).
And once you're done parsing, executing "block by block" will generally not work. You'll need to keep track of environment variable changes, current directory changes, etc...
That being said, have you looked at things like jbash?
Upvotes: 1