Ez a bejegyzés eredetileg Stephen weboldalán jelent meg, és itt újra megjelenik (kissé átírva), engedéllyel
Bevezetés a cal használatába
Tudtad, hogy a macOS és a Linux parancssorában van naptár?
$ cal
July 2021
Su Mo Tu We Th Fr Sa
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Egy gyors pillantás a tldr-be megmutatja, milyen hasznos lehet ez a segédprogram
$ tldr cal
cal
Prints calendar information.
- Display a calendar for the current month:
cal
- Display previous, current and next month:
cal -3
- Display a calendar for a specific month (1-12 or name):
cal -m month
- Display a calendar for the current year:
cal -y
- Display a calendar for a specific year (4 digits):
cal year
- Display a calendar for a specific month and year:
cal month year
- Display date of Easter (Western Christian churches) in a given year:
ncal -e year
De van egy dolog, ami hiányzik belőle. A szökőévek!
A cal persze tud a szökőévekről, különben nem lenne valami jó naptár. Arra viszont nincs módja, hogy kilistázza őket.
Szökőévek felismerése hekkelős módon
Építsük meg mi magunk! Persze használhatnánk egy rendes programozási nyelvet naptárkönyvtárral, de szórakoztató dolgokat építeni parancssori összetevőkből, és úgysem fogjuk éles környezetben használni ezt a számítást.
Először is egyezzünk meg abban, hogy a február jelzi a szökőévet.
$ cal 02 2021
February 2021
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28
$ cal 02 2020
February 2020
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
Megkereshetnénk a „29”-et, és abból felismerné a szökőéveket? Talán!
$ cal 02 2020 | grep -q 29 && echo "LEAP" || echo "normal"
LEAP
$ cal 02 2021 | grep -q 29 && echo "LEAP" || echo "normal"
normal
Eddig jó? De fogadok, hogy látod a problémát.
$ for year in {2020..2030}; do
printf "$year ";
cal 02 "$year" | grep -q 29 && echo "LEAP" || echo "year";
done
2020 LEAP
2021 year
2022 year
2023 year
2024 LEAP
2025 year
2026 year
2027 year
2028 LEAP
2029 LEAP
2030 year
A 2029 NEM szökőév. Csak azért illeszkedik, mert a 2029-ben szerepel a 29.
$ cal 02 2029
February 2029
Su Mo Tu We Th Fr Sa
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28
Ezt a kellemetlen találatot úgy szüntethetnénk meg, hogy a "February" szót tartalmazó egész sort kivesszük a vizsgálatból.
$ cal 02 2029 | grep -v Feb
Su Mo Tu We Th Fr Sa
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28
Most azt állítom, hogy ha találunk egy „29”-et az eredményben, akkor az TÉNYLEG szökőév.
$ for year in {2020..2030}; do
printf "$year ";
cal 02 "$year" | grep -v Feb | grep -q 29 && echo "LEAP" || echo "year";
done
2020 LEAP
2021 year
2022 year
2023 year
2024 LEAP
2025 year
2026 year
2027 year
2028 LEAP
2029 year
Ez megegyezik a tényleges szökőévekkel?
A Wolfram Research Leap Year oldalán ez a lista szerepel a 21. század első felének szökőéveiként.
2000
2004
2008
2012
2016
2020
2024
2028
2032
2036
2040
2044
2048
Lássuk, hogy teljesítünk
$ for year in {2000..2051}; do cal 02 "$year" | grep -v Feb | grep -q 29 && echo $year; done
2000
2004
2008
2012
2016
2020
2024
2028
2032
2036
2040
2044
2048
Kész!
De ez a grep-elés a 29-re túl egyszerű. Legyünk még furcsábbak!
Szökőévek felismerése, még hekkelősebb módon
A szökőév és a normál év között a különbség az a plusz nap, a 29-e. Felismerhetnénk a szökőéveket a cal kimenetében lévő karakterek megszámolásával?
Igen, megtehetjük. De előbb van egy buktató, amin túl kell jutni. Megmutatom, mire gondolok.
Tudjuk, hogy a 2020 szökőév, a 2021 pedig nem. Hasonlítsuk össze, hány karaktert ad ki a február ezekben az években. Ehhez a hasznos wc segédprogramot használjuk, pontosabban a wc -c-t, ami a bájtokat számolja.
$ printf "hello" | wc -c
5
5 karakter a „hello”-ban (azért printf, nem echo, mert különben egy sortörés karakter is lenne). Nagyszerű!
$ cal 02 2020 | wc -c
184
$ cal 02 2021 | wc -c
184
Itt a buktató. Minden hónap kimenete más (a 2020-nál ott van a 29. nap), de mindkettő ugyanannyi karakterből áll, mert a cal szóközöket szúr be a következetes formázás érdekében. A szóközök ugyanúgy megszámolható karakterek, mint a számok.
Mit tehetünk?
Eltávolíthatjuk az összes szóközt! Így csak azok a karakterek maradnak, amiket meg akarunk számolni.
A karakterek eltávolítására nagyszerű segédprogram a tr parancs. A tr parancs string adatokat alakít át. Például az összes a karaktert A karakterré változtathatja.
$ echo "aardvark" | tr a A
AArdvArk
Az összes megadott karaktert is törölheti, ami pontosan az, amit szeretnénk.
A mi esetünkben az összes whitespace karaktert törölni akarjuk, bármi is legyen az (szóközök, tabulátorok, sortörések stb.). Van egy hasznos csoportosítás, a karakterosztály, amivel pontosan ezt tehetjük meg. A [:space:] karakterosztály mindezeket a különböző whitespace karaktereket célozza.
$ cal 02 2020 | tr -d '[:space:]'
February2020SuMoTuWeThFrSa1234567891011121314151617181920212223242526272829
$ cal 02 2021 | tr -d '[:space:]'
February2021SuMoTuWeThFrSa12345678910111213141516171819202122232425262728
Nagyszerű! Megszámolhatjuk most a karaktereket, hogy megtaláljuk a szökőéveket? Hát persze!
$ cal 02 2020 | tr -d '[:space:]' | wc -c
75
$ cal 02 2021 | tr -d '[:space:]' | wc -c
73
Most pedig írjunk ki néhány szökőévet!
$ for year in {2000..2051}; do
cal 02 "$year" |
tr -d '[:space:]' |
wc -c |
grep -q 75 && echo "$year";
done
2000
2004
2008
2012
2016
2020
2024
2028
2032
2036
2040
2044
2048
Hackek hátán hackek, és működik!
Szerinted melyik a gyorsabb? Hát nekem sem sok fogalmam van róla. A „February eltávolítása” útvonal talán gyorsabb? De meglepődnék, ha nagy lenne a különbség.
Ne lepődjünk meg. Nézzük meg!
Van egy nagyszerű segédprogram, a hyperfine, amivel több parancssori hívás teljesítményét lehet kiértékelni
Melyik a gyorsabb? A Hyperfine megmondja
Íme a kapcsolók, amiket a hyperfine-nak adok a két paranccsal együtt.
--style basic
Plain output styling
--export-markdown hyperfine.md
Export results as markdown
--warmup 5
Do five runs before benchmarking
--ignore-failure
Ignore non-zero exits
Minket csak egy év kiszámításának teljesítménymérése érdekel, nem az, mennyi idő az évek ciklusának előállítása. Ezért csak egy adott év szökőév-ellenőrzését mérem le mindkét megközelítéssel.
$ hyperfine --style basic --export-markdown hyperfine.md --warmup 5 --ignore-failure "cal 02 2050 | grep -v Feb | grep -q 29" "cal 02 2050 | tr -d '[:space:]' | wc -c | grep -q 75"
Benchmark #1: cal 02 2050 | grep -v Feb | grep -q 29
Time (mean ± σ): 1.8 ms ± 0.3 ms [User: 0.7 ms, System: 1.9 ms]
Range (min … max): 1.0 ms … 3.6 ms 493 runs
Warning: Command took less than 5 ms to complete. Results might be inaccurate.
Warning: Ignoring non-zero exit code.
Benchmark #2: cal 02 2050 | tr -d '[:space:]' | wc -c | grep -q 75
Time (mean ± σ): 1.9 ms ± 0.4 ms [User: 0.9 ms, System: 2.7 ms]
Range (min … max): 0.7 ms … 5.9 ms 811 runs
Warning: Command took less than 5 ms to complete. Results might be inaccurate.
Warning: Ignoring non-zero exit code.
Summary
'cal 02 2050 | grep -v Feb | grep -q 29' ran
1.07 ± 0.28 times faster than 'cal 02 2050 | tr -d '[:space:]' | wc -c | grep -q 75'
| Parancs | Átlag [ms] | Min [ms] | Max [ms] | Relatív |
|---|---|---|---|---|
cal 02 2050 | grep -v Feb | grep -q 29 |
1.8 ± 0.3 | 1.0 | 3.6 | 1.00 |
cal 02 2050 | tr -d '[:space:]' | wc -c | grep -q 75 |
1.9 ± 0.4 | 0.7 | 5.9 | 1.07 ± 0.28 |
Ahogy vártuk, gyakorlatilag nincs különbség köztük a valós időt tekintve, DE a „February eltávolítása” útvonal talán valamivel gyorsabb.
Ezzel véget is ér ez a parancssori eszköz-összeállítási utazás.
Összegzés
- Új parancssori funkciókat építhetsz úgy, hogy meglévő eszközöket egy adatcsővezetékbe fűzöl
- Egy kis kreativitással meglepően összetett eredményeket hozhatsz ki egyszerű darabokból
- A Hyperfine hasznos eszköz parancssori parancsok teljesítménymérésére
Következő lépések
- Fedezd fel a
calparancs használatát. - Próbálj építeni egy csővezetéket, amely megszámolja egy egész év összes nem szóköz karakterét:
cal 2020 - Próbáld lefuttatni ezt az egész éves számláló csővezetéket az 1750 és 1759 közötti évekre: feltűnik valami furcsa?