Skip to content

Instantly share code, notes, and snippets.

@cescoffier
Created June 26, 2023 09:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cescoffier/80cd4659895df854883cf1a3448e1390 to your computer and use it in GitHub Desktop.
Save cescoffier/80cd4659895df854883cf1a3448e1390 to your computer and use it in GitHub Desktop.
A JBang script to edit your /etc/hosts file (add and remove entries)
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS info.picocli:picocli:4.7.4
import java.io.File;
import java.io.IOException;
import java.lang.System.Logger.Level;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import picocli.CommandLine;
public class HostEdit implements Callable<Integer> {
private static final String SECTION_ENTRY = "# Host Edit Section";
private static final String SECTION_EXIT = "# End of Host Edit Section";
private static final System.Logger LOGGER = System.getLogger("Host Edit");
@CommandLine.Option(names = { "-h","--host-file" },
description = "The path to the host file", defaultValue = "/etc/hosts")
private File hosts;
@CommandLine.Option(names = { "--ip" }, description = "The ip to add to the hosts file")
private String ip;
@CommandLine.Option(names = { "--host" }, description = "The host name to add or remove", required = true)
private String host;
@CommandLine.Option(names = { "--dry-run" }, description = "Do not write the file, just print the outcome", defaultValue="false")
private boolean dryRun;
public static void main(String... args) {
var exitCode = new CommandLine(new HostEdit()).execute(args);
System.exit(exitCode);
}
@Override
public Integer call() throws Exception {
if (!ensureWritable()) {
return 1;
}
var lines = read();
if (ip != null) {
LOGGER.log(Level.INFO, "Adding `{0} {1}` to the hosts file", ip, host);
lines = insert(lines, ip, host);
} else if (host != null) {
LOGGER.log(Level.INFO, "Removeing line with `{0}` from the hosts file", host);
lines = remove(lines, host);
}
if (dryRun) {
LOGGER.log(Level.INFO, "Resulting {0} file", hosts);
System.out.println();
lines.forEach(l -> {
System.out.println("\t" + l);
});
} else {
Files.write(hosts.toPath(), lines);
}
return 0;
}
private List<String> insert(List<String> lines, String ip, String host) {
boolean inSection = false;
boolean endOfSection = false;
List<String> output = new ArrayList<>();
for (String line : lines) {
if (line.trim().equals(SECTION_ENTRY)) {
inSection = true;
output.add(line);
} else if (inSection && line.trim().equals(SECTION_EXIT)) {
// End of section
output.add(ip + " " + host);
endOfSection = true;
output.add(line);
} else {
output.add(line);
}
}
if (inSection && ! endOfSection) {
output.add(SECTION_EXIT);
}
if (! inSection) {
output.add(SECTION_ENTRY);
output.add(ip + " " + host);
output.add(SECTION_EXIT);
}
return output;
}
private List<String> remove(List<String> lines, String host) {
boolean inSection = false;
boolean endOfSection = false;
boolean found = false;
String removed = null;
int count = 0;
List<String> output = new ArrayList<>();
for (String line : lines) {
if (line.trim().equals(SECTION_ENTRY)) {
inSection = true;
output.add(line);
} else if (inSection && line.trim().equals(SECTION_EXIT)) {
// End of section
endOfSection = true;
output.add(line);
} else if (inSection && ! endOfSection && line.contains(host)) {
// skip line
found = true;
removed = line;
}else if (inSection && !endOfSection) {
output.add(line);
count = count + 1;
} else {
output.add(line);
}
}
if (! found) {
LOGGER.log(Level.ERROR, "Unable to find the line with `{0}` in the hosts file", host);
} else {
LOGGER.log(Level.INFO, "The line `{0}` has been removed", removed);
if (count == 0) {
LOGGER.log(Level.INFO, "Removing section");
output = removeSection(lines);
}
}
return output;
}
private List<String> removeSection(List<String> lines) {
List<String> output = new ArrayList<>();
boolean inSection = false;
boolean endOfSection = false;
for(String line : lines) {
if (line.trim().equals(SECTION_ENTRY)) {
// Skip
inSection = true;
} else if (inSection && ! endOfSection && line.trim().equals(SECTION_EXIT)) {
endOfSection = true;
} else if (! inSection && ! endOfSection) {
output.add(line);
}
}
return output;
}
private List<String> read() throws IOException {
return Files.readAllLines(hosts.toPath());
}
private boolean ensureWritable() {
if (!hosts.isFile()) {
LOGGER.log(Level.ERROR, "The host file ({0}) does not exist", hosts.getAbsolutePath());
return false;
}
if (!hosts.canWrite()) {
LOGGER.log(Level.ERROR, "The host file ({0}) cannot be written, try running with elevated privileges.",
hosts.getAbsolutePath());
if (dryRun) {
LOGGER.log(Level.WARNING, "The host file ({0}) is not writable, but dry-run is enabled, so continuing...", hosts.getAbsolutePath());
return true;
}
return false;
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment