Preskoči na sadržaj

Zašto ne sed -i / date -d / readlink -f u shell skriptama?

TL;DR

Pipeline runner-i su i macOS (BSD userland) i Linux (GNU userland). Isti shell flag na jednom radi, na drugom puca ili tiho korumpira fajl. Pravilo: FS / date / path u Pythonu, ne u shellu. Shell je samo za orkestraciju.

Tri zamke koje troše dane

sed -i — prazan string vs bez argumenta

# macOS (BSD) zahtijeva prazan string kao backup ekstenziju
sed -i '' 's/foo/bar/' file.txt

# Linux (GNU) NE SMIJE imati argument
sed -i 's/foo/bar/' file.txt

Ove dvije komande se jedna na drugu ruše bez greške. macOS GNU flag tretira kao ime fajla, GNU macOS flag tretira kao komandu. Rezultat: ništa se ne promijeni, ili se kreira file.txt-e backup, ili se sve obriše.

Fix: Python (pathlib.Path.write_text ili tempfile.NamedTemporaryFile + os.replace). Portable, atomic, čitljiv.

date -d (GNU) vs date -v (BSD)

# GNU: "prije 3 dana"
date -d "3 days ago" +%Y-%m-%d

# BSD: ista stvar, drugi flag
date -v -3d +%Y-%m-%d

Isti cilj, potpuno druga sintaksa. CI skript koji prođe na macOS-u pukne na Linuxu (ili obrnuto).

Fix: Python datetime + timedelta. Isti kod svugdje.

# GNU
readlink -f /path/to/file

# BSD: NE POSTOJI. Treba:
python3 -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$path"

readlink bez -f na BSD-u ne prati lanac — readlink -f je GNU ekstenzija koja nema BSD ekvivalent.

Fix: python3 -c 'import os, sys; print(os.path.realpath(...))'.

Zašto je bitno

ci-artifacts se vrti na:

  • Self-hosted macOS (Mac mini) — BSD userland
  • Self-hosted Linux (Hetzner) — GNU userland

Ista skripta mora raditi na oba. Shell trik optimizovan za jedan OS tiho korumpira produkciju na drugom. Najgori slučajevi su "tiho radi" — pipeline prođe, ali deploy je pogrešan.

Defanzivna pravila

  • Sve FS mutacije kroz pathlib.Path + os.replace (atomic).
  • Sve date math kroz datetime + timedelta.
  • Sve path resolution kroz os.path.realpath (ili pathlib).
  • OS provjera kroz runner_detect.is_macos() / is_linux(), ne inline platform.system() == "Darwin".

Ili još bolje: ne treba ti OS check ako koristiš stdlib Python koji je cross-platform.

Kada shell JESTE u redu

  • Orkestracija: make, if, for, case
  • Testiranje exit kodova: cmd && echo OK || echo FAIL
  • Globs i redirekcije

Ono što NE radimo u shellu:

  • FS mutacije
  • Date manipulacije
  • Path resolution
  • JSON parsing
  • HTTP pozive (uvek u vendor/api/api.py)

Vidi i