Created
June 26, 2023 09:06
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///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