Skip to content

Instantly share code, notes, and snippets.

@bbarenblat
Created April 10, 2018 18:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbarenblat/75aee6ac264b5d93b674e125d63f2051 to your computer and use it in GitHub Desktop.
Save bbarenblat/75aee6ac264b5d93b674e125d63f2051 to your computer and use it in GitHub Desktop.
#!/bin/sh
# Copyright 2017 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# pwrite collects output from multiple commands running in parallel, appending
# the output to the given file. It ensures that outputs are not interleaved. For
# example,
#
# printf "1\n2\n" | xargs -P 0 -n 1 sh -c "(echo one; sleep 0.1; echo two) | pwrite /tmp/out"
#
# will always append
#
# one
# two
# one
# two
#
# to /tmp/out; it will never append
#
# one
# one
# two
# two
#
# as would likely occur with a simple shell redirection.
set -eu
if [ $# -ne 1 ]; then
echo >&2 "usage: $0 file"
exit 1
fi
# Open the output file on FD 3.
readonly OUTPUT_FD=3
eval "exec $OUTPUT_FD>>'$1'"
if flock -n $OUTPUT_FD; then
# We got a lock on the output file. Write straight to it.
exec cat >&$OUTPUT_FD
fi
# We couldn't take the lock. Buffer stdin to a temporary file.
readonly tmpdir="$1.pwrite"
mkdir -p "$tmpdir"
trap 'set +e; rmdir "$tmpdir" 2>/dev/null; true' EXIT
readonly tmp="$tmpdir/$$"
cat >"$tmp"
# There's no more input. Wait until we can grab the lock on the output file,
# and append the input.
if ! flock $OUTPUT_FD; then
echo >&2 "$0: failed to take lock; output is in $tmp"
exit 1
fi
cat <"$tmp" >&$OUTPUT_FD
rm "$tmp"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment