Topic: scripting
-
PowerShell error handling for admin scripts: terminating errors, $ErrorActionPreference and $LASTEXITCODE
PowerShell has two error classes that behave differently in a script: terminating errors that try/catch can stop, and non-terminating errors that only $ErrorActionPreference or -ErrorAction can turn into a stop. A third class, failures from native executables, follows neither path unless $PSNativeCommandUseErrorActionPreference is set (PowerShell 7.4+, experimental in 7.3).
-
Writing a portable POSIX sh script across Linux, BSD, macOS and AIX
A script meant to run unchanged on Debian's dash, a Linux box's bash-as-sh, FreeBSD, macOS and AIX has to stay inside the POSIX Shell Command Language and avoid GNU-only tool flags; checkbashisms and a short list of GNU-versus-BSD command differences catch most of what breaks.
-
Logging from admin scripts: logger and systemd-cat on Linux, Write-EventLog/New-WinEvent and Start-Transcript on Windows
Every platform has a way to put an admin script's output where a human or another tool will find it later, distinct from writing to stdout: logger(1) or systemd-cat into the system log on Linux, the Windows Event Log via Write-EventLog (Windows PowerShell 5.1 only) or New-WinEvent, and Start-Transcript for a raw session capture. None of them should ever receive a secret.
-
Dry-run and confirmation in admin scripts: PowerShell -WhatIf/-Confirm and a shell --dry-run flag
PowerShell has a built-in mechanism, SupportsShouldProcess, that gives a custom function real -WhatIf and -Confirm switches wired to the same $PSCmdlet.ShouldProcess() call the built-in cmdlets use. Shell scripts have no equivalent built in, so a --dry-run flag has to be threaded through by hand.
-
Writing idempotent admin scripts: check-then-change, drop-in files and marker comments
An admin script an agent might re-run after a partial failure or a repeated schedule should leave the system in the same state whether it runs once or five times. Checking current state before changing it, writing separate drop-in files instead of editing shared ones, and marking any block a script owns are the three patterns that make that reliable.
-
Running a command as another user from a script: sudo -u, runuser, su -c, and PowerShell's -Credential
sudo -u, runuser and su -c all run a command with a substitute user and group ID but differ in whether they need the target's password, whether they load a login environment, and who is allowed to invoke them; PowerShell's Start-Process and remoting cmdlets take a -Credential object instead, and none of these should ever receive a password through a pipe or a command-line argument.
-
zsh as the macOS default shell: what breaks in a script written for bash
macOS has used zsh as the default login and interactive shell since macOS 10.15; a script that assumes bash's word-splitting, zero-based arrays or forgiving globbing can behave differently or fail outright under an agent's interactive zsh session on a Mac, even before /bin/bash's own age is considered.
-
Using SMIT as a command generator instead of a menu system
smit/smitty menus ultimately run ordinary AIX commands, and the tool is built to show you which ones: the output window displays the constructed command statement, and smit.log/smit.script capture every command and its exact flags with timestamps. Reading smit.script after a menu-driven task is a fast way to learn the equivalent non-interactive command.
-
cmd.exe and batch script pitfalls: %ERRORLEVEL% timing, delayed expansion and caret escaping
Batch files still show up inside installers, legacy scheduled tasks and vendor tooling. %ERRORLEVEL% is expanded at parse time and can read stale, delayed expansion changes that behaviour, and the caret is the escape character that most agent-generated batch code gets wrong on the first try.
-
Exit codes as a contract between scripts and the agent that calls them
An agent that runs a script and moves on based on its exit code needs that code to mean what the shell's own conventions say it means: 0 for success, small positive numbers for the script's own defined failures, 126/127 for a command that could not be run at all, and 128+n for death by signal — and it needs pipefail or PIPESTATUS to see the real code through a pipeline.
Machine-readable: JSON