Odczyt argumentów wiersza poleceń
- Przydatne narzędzia
- poziom: średni
- aktualizacja: 16 sierpnia 2026
Program uruchamiany z konsoli dostaje jedną tablicę String[] args — i tyle, reszta należy do Ciebie. Odczytywanie jej po indeksach, args[0] jako plik, args[1] jako tryb, wywraca się przy pierwszej przestawionej opcji, a własny łańcuch if-ów po startsWith("-") gubi się przy pierwszej sklejonej flagce. Ta klasa układa tablicę w trzy przewidywalne rzeczy: mapę opcji z wartościami, zbiór flag i listę argumentów pozycyjnych. Rozumie obie formy zapisu wartości, sklejone flagi krótkie oraz POSIX-owy separator --, po którym każdy kolejny argument jest już zwykłą nazwą pliku — także taką zaczynającą się od myślnika, którą bez separatora każdy parser rozłoży na flagi.
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Parser argumentów wiersza poleceń — skryptyJAVA.pl
* Zapisz jako: ArgumentyPolecen.java
*/
public class ArgumentyPolecen {
private final Map<String, String> wartosci = new LinkedHashMap<>();
private final Set<String> flagi = new LinkedHashSet<>();
private final List<String> pozycyjne = new ArrayList<>();
/** W zWartoscia podaj nazwy opcji bez myślników (np. "wyjscie", "o"), które biorą wartość. */
public ArgumentyPolecen(String[] args, String... zWartoscia) {
Set<String> zWartosciami = Set.of(zWartoscia);
boolean koniecOpcji = false;
for (int i = 0; i < args.length; i++) {
String argument = args[i];
if (!koniecOpcji && argument.equals("--")) {
koniecOpcji = true; // od tego miejsca wszystko jest pozycyjne
} else if (koniecOpcji || argument.length() < 2 || !argument.startsWith("-")) {
pozycyjne.add(argument); // samotny "-" to zwykle strumień, nie opcja
} else if (argument.startsWith("--")) {
i = czytajDluga(argument.substring(2), args, i, zWartosciami);
} else {
i = czytajKrotkie(argument.substring(1), args, i, zWartosciami);
}
}
}
/** Obsługuje --nazwa=wartosc i --nazwa wartosc; zwraca indeks ostatniego zużytego argumentu. */
private int czytajDluga(String tresc, String[] args, int i, Set<String> zWartosciami) {
int rownosc = tresc.indexOf('=');
if (rownosc >= 0) {
wartosci.put(tresc.substring(0, rownosc), tresc.substring(rownosc + 1));
} else if (!zWartosciami.contains(tresc)) {
flagi.add(tresc);
} else if (i + 1 < args.length && !args[i + 1].equals("--")) {
wartosci.put(tresc, args[i + 1]);
i++; // wartość zabrana z kolejnego argumentu
} else {
throw new IllegalArgumentException("Opcja --" + tresc + " wymaga wartości.");
}
return i;
}
/** Obsługuje -v, sklejone flagi -vf oraz krótką opcję z wartością: -o plik i -oplik. */
private int czytajKrotkie(String litery, String[] args, int i, Set<String> zWartosciami) {
for (int k = 0; k < litery.length(); k++) {
String nazwa = String.valueOf(litery.charAt(k));
if (!zWartosciami.contains(nazwa)) {
flagi.add(nazwa);
} else if (k + 1 < litery.length()) {
wartosci.put(nazwa, litery.substring(k + 1)); // reszta sklejki jest wartością
return i;
} else if (i + 1 < args.length && !args[i + 1].equals("--")) {
wartosci.put(nazwa, args[i + 1]);
return i + 1;
} else {
throw new IllegalArgumentException("Opcja -" + nazwa + " wymaga wartości.");
}
}
return i;
}
// Odczyt: tekst() i liczba() mają wartość domyślną, wymagany() przerywa działanie programu.
public boolean flaga(String nazwa) { return flagi.contains(nazwa); }
public String tekst(String nazwa, String domyslna) { return wartosci.getOrDefault(nazwa, domyslna); }
public List<String> pozycyjne() { return List.copyOf(pozycyjne); }
public int liczba(String nazwa, int domyslna) {
String wartosc = wartosci.get(nazwa);
if (wartosc == null) { return domyslna; }
try {
return Integer.parseInt(wartosc.trim());
} catch (NumberFormatException blad) { // literówka ma dać komunikat, a nie ciche zero
throw new IllegalArgumentException("Opcja --" + nazwa + " oczekuje liczby: " + wartosc);
}
}
public String wymagany(String nazwa) {
String wartosc = wartosci.get(nazwa);
if (wartosc == null || wartosc.isBlank()) {
throw new IllegalArgumentException("Brak wymaganej opcji --" + nazwa + ". Zobacz --pomoc.");
}
return wartosc;
}
public static String pomoc() {
return """
Użycie: java ArgumentyPolecen [opcje] [--] plik...
--wyjscie=PLIK | --wyjscie PLIK plik wynikowy (wymagany)
--szerokosc N szerokość kolumny (domyślnie 72)
-o PLIK | -oPLIK krótka opcja z wartością
--tryb-cichy, -v, -f flagi logiczne, można łączyć: -vf
--pomoc ten opis
Wszystko po samotnym -- jest nazwą pliku, nawet gdy zaczyna się od myślnika.""";
}
private static void pokaz(String opis, String... args) {
System.out.println("== " + opis);
try {
ArgumentyPolecen opcje = new ArgumentyPolecen(args, "wyjscie", "szerokosc", "o");
if (opcje.flaga("pomoc")) { System.out.println(pomoc()); return; }
System.out.println(" wyjście=" + opcje.wymagany("wyjscie") + ", szerokość="
+ opcje.liczba("szerokosc", 72) + ", -o=" + opcje.tekst("o", "brak"));
System.out.println(" cichy/v/f=" + opcje.flaga("tryb-cichy") + "/" + opcje.flaga("v")
+ "/" + opcje.flaga("f") + ", pliki=" + opcje.pozycyjne());
} catch (IllegalArgumentException blad) {
System.out.println(" Błąd: " + blad.getMessage()); // tak reaguje program konsolowy
}
}
// ── PRZYKŁADY ───────────────────────────────────────────
public static void main(String[] args) {
if (args.length > 0) { pokaz("argumenty z konsoli", args); return; }
pokaz("pełne wywołanie", "--wyjscie=raport.txt", "--szerokosc", "100", "-owynik.txt",
"--tryb-cichy", "-vf", "dane.csv", "--", "--plik-z-myslnikiem.csv");
pokaz("pusta tablica args");
pokaz("zła liczba", "-o", "wynik.txt", "--wyjscie", "raport.txt", "--szerokosc=sto");
pokaz("prośba o pomoc", "--pomoc");
}
}
Jak użyć tego skryptu
- Wklej kod do pliku
ArgumentyPolecen.java(nazwa pliku musi odpowiadać klasie publicznej) i wywołajjava ArgumentyPolecen.java. Bez podania czegokolwiek zobaczysz cztery wywołania przygotowane w kodzie: pełne, z pustą tablicą, z błędną liczbą i z--pomoc. Możesz też dopisać własne argumenty za nazwą pliku — trafią prosto do parsera. - W swoim programie utwórz obiekt w pierwszej linii metody
main, np.new ArgumentyPolecen(args, "wyjscie", "szerokosc"). Nazwy wymienione po tablicy to opcje, po których parser sięgnie po wartość do kolejnego argumentu; niezadeklarowany argument z myślnikiem ląduje wśród flag, chyba że sam niesie znak równości — wtedy i tak trafia do mapy wartości. - Wartości odczytuj przez
tekst(nazwa, domyslna),liczba(nazwa, domyslna)iflaga(nazwa), a to, bez czego program nie ruszy — przezwymagany(nazwa). Nazwy plików odbierzesz z metodypozycyjne()jakoList<String>, w kolejności podania. - Zanim zaczniesz liczyć cokolwiek, sprawdź
flaga("pomoc")i wypiszpomoc(), a wyjątekIllegalArgumentExceptionzłap wmaini pokaż sam komunikat — użytkownik konsoli ma zobaczyć jedno zdanie, nie stos wywołań. Separator--nie może być wartością w formie z odstępem; dosłowną wartość „--” podaj jako--wyjscie=--albo-o--. Gdy ta sama opcja pojawi się dwa razy, obowiązuje ostatnia wartość.
Warto wiedzieć
- Wszystko, co przychodzi w
args, jest danymi wejściowymi od użytkownika — parser wyłącznie je rozdziela, nie sprawdza sensu. Zanim użyjesz wartości, zweryfikuj ją tak samo jak pole formularza: przypilnuj zakresu liczby, sprawdź przezPath.normalize(), czy ścieżka nie ucieka poza katalog roboczy, i nie doklejaj tekstu z argumentu do zapytania SQL ani do polecenia powłoki. - Nie przekazuj w argumentach haseł, tokenów ani kluczy API. Wiersz polecenia uruchomionego procesu widzi każdy użytkownik maszyny — w Menedżerze zadań wystarczy włączyć kolumnę „Wiersz polecenia", w systemach uniksowych pokaże go
ps -ef— a powłoka dopisze całe wywołanie do swojej historii i do logów serwera CI. Sekret podaj zmienną środowiskową albo plikiem z ograniczonymi prawami dostępu. - Parser sam nie zgadnie, które opcje biorą wartość. Jeśli nie wymienisz nazwy w konstruktorze, wywołanie
--wyjscie raport.txtzapisze flagę „wyjscie", araport.txtwyląduje wśród argumentów pozycyjnych — i program po cichu policzy coś innego, niż chciałeś. Forma--wyjscie=raport.txtjest odporna na to przeoczenie, bo znak równości rozstrzyga sprawę na miejscu. - Ten kod celowo kończy się tam, gdzie zaczyna się biblioteka: nie zna podkomend ani typów innych niż tekst i liczba całkowita, a treść metody
pomoc()piszesz ręcznie — dopisanie opcji do konstruktora nie zmieni w niej ani znaku, więc opis rozjedzie się z kodem przy pierwszym pośpiechu. Przy narzędziu z kilkunastoma opcjami sięgnij po picocli albo JCommander — opcje deklarujesz adnotacjami przy polach, a konwersję typów, komunikaty o błędach i--helpdostajesz gotowe.
Podobne skrypty
Generator bezpiecznych haseł
Generator haseł oparty na klasie SecureRandom: wybór długości i zestawów znaków, gwarancja użycia każdego zestawu i opcja pomijania mylących znaków (l, I, 1, O, 0).
początkujący Zobacz kod → Przydatne narzędziaHashowanie i weryfikacja haseł przez PBKDF2
Hasło w bazie ma być nieczytelne nawet dla Ciebie. PBKDF2WithHmacSHA256 z losową solą, kosztem iteracyjnym, przenośnym formatem rekordu i porównaniem odpornym na analizę czasu — bez zewnętrznych bibliotek, na czystym JDK 17.
zaawansowany Zobacz kod → Przydatne narzędziaSzyfrowanie i odszyfrowanie przez AES-GCM
Uwierzytelnione szyfrowanie tekstu algorytmem AES-GCM: klucz 256-bitowy, losowy 96-bitowy IV, dane powiązane AAD i przenośny zapis Base64. Ktoś przestawi jeden bit szyfrogramu — odszyfrowanie się nie uda, zamiast po cichu zwrócić przekłamaną treść.
zaawansowany Zobacz kod →