Skip to content

tennox/test-install-portability

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Install Stdin Portability Test Results

This repository tests different approaches for installing executable scripts using the install(1) command, with a focus on cross-platform portability between Linux (GNU coreutils) and macOS (BSD).

Test Results

Latest test run: GitHub Actions Workflow

Summary

All required portability tests PASSED on both Linux and macOS.

Basic Functionality

Approach Linux (Ubuntu 24.04) macOS (Darwin 24.6)
install -m 755 /dev/stdin <<EOF ✅ Works ✅ Works
install -m 755 /dev/fd/0 <<EOF ✅ Works ✅ Works
cat >file && chmod 755 ✅ Works ✅ Works
tempfile + install -m 755 ✅ Works ✅ Works

Foreign-Owned File Handling

When replacing a file owned by another user (e.g., root-owned in user-writable directory):

Approach Linux macOS Outcome
cat >file && chmod ⚠️ chmod denied ⚠️ chmod denied File updated but NOT executable
install /dev/stdin ✅ Works ✅ Works File replaced, executable, new owner
tempfile + install ✅ Works ✅ Works File replaced, executable, new owner

Platform Details

Linux (Ubuntu 24.04.3 LTS)

  • install command: GNU coreutils 9.4
  • Location: /usr/bin/install
  • Kernel: Linux 6.11.0-1018-azure

macOS (Darwin 24.6.0)

  • install command: BSD install
  • Location: /usr/bin/install
  • OS: macOS 15.7.2

Key Findings

✅ Recommended Approaches (Portable)

Both of these patterns work reliably across Linux and macOS:

1. Heredoc with /dev/stdin (Recommended for inline content)

install -m 755 /dev/stdin target <<'EOF'
#!/usr/bin/env bash
echo "script content"
EOF

Pros:

  • Works on both Linux and macOS
  • Single command (atomic)
  • Properly handles ownership changes on foreign-owned files
  • Clean syntax for embedding scripts

Cons:

  • None identified

2. Temporary file + install (Recommended for variable content)

tmp=$(mktemp)
printf '%s\n' "$SCRIPT_CONTENT" > "$tmp"
install -m 755 "$tmp" target
rm -f "$tmp"

Pros:

  • Works on both Linux and macOS
  • Properly handles ownership changes on foreign-owned files
  • Good for content in variables
  • Most traditional approach

Cons:

  • Requires cleanup of temporary file
  • Multiple commands (not atomic)

🔍 Alternative: /dev/fd/0

The pattern install -m 755 /dev/fd/0 target <<EOF also works on both platforms and is functionally equivalent to /dev/stdin.

❌ Non-Portable: Piped stdin

The pattern echo "content" | install -m 755 /dev/stdin target has platform-dependent behavior:

  • Linux (GNU coreutils): ✅ Works correctly
  • macOS (BSD install): ❌ Fails with exit code 71

Do not use this pattern if cross-platform compatibility is needed.

🚨 Broken Pattern: cat + chmod

The classic cat >file && chmod 755 file pattern has a critical flaw when the target file is owned by another user (e.g., root):

# If 'target' is owned by root but world-writable:
cat >target <<EOF  # ✅ Succeeds (file is writable)
#!/bin/bash
echo "new content"
EOF

chmod 755 target   # ❌ FAILS (you don't own the file)

This leaves the file with the new content but without execute permissions, breaking the script.

Both install approaches (heredoc and tempfile) solve this problem by atomically replacing the file, including ownership.

Foreign-Owned File Behavior

When replacing a file owned by another user (e.g., a root-owned file in a user-writable directory):

Method Behavior Result
cat >file && chmod ❌ chmod fails File updated but not executable
install /dev/stdin ✅ Replaces file New owner (current user), mode 755
tempfile + install ✅ Replaces file New owner (current user), mode 755

Both install methods properly handle ownership transfer, while cat + chmod breaks.

Test Script

The test suite (test-install-portability.sh) validates:

  1. Basic functionality of each approach
  2. Proper handling of foreign-owned files (requires sudo)
  3. Platform-specific behaviors

Run locally:

./test-install-portability.sh

Recommendations

  1. For inline script content: Use install -m 755 /dev/stdin target <<EOF
  2. For variable content: Use temporary file + install -m 755 "$tmp" target
  3. Avoid: Piped stdin (echo | install /dev/stdin) for cross-platform code
  4. Never use: cat >file && chmod when file ownership might change

References

About

test repo for install tool

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages