sharxz 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #!/usr/bin/env bash
  2. sharxz(){
  3. TAR_OPTIONS=()
  4. CHDIR=""
  5. CMD=""
  6. ARGS=()
  7. OUTPUT="-"
  8. DEBUG=false
  9. PARSE=false
  10. ADD_PARSER=false
  11. KEEP=false
  12. INTERACTIVE=false
  13. INSTALL_DIR=""
  14. while getopts ":dikpPI:T:o:C:x:" opt; do
  15. case $opt in
  16. d )
  17. DEBUG=true
  18. ;;
  19. i )
  20. INTERACTIVE=true
  21. ;;
  22. k )
  23. KEEP=true
  24. ;;
  25. p )
  26. PARSE=true
  27. ;;
  28. P )
  29. ADD_PARSER=true
  30. ;;
  31. I )
  32. INSTALL_DIR="$OPTARG"
  33. ;;
  34. T )
  35. TAR_OPTIONS+=( $OPTARG )
  36. ;;
  37. o )
  38. OUTPUT="$OPTARG"
  39. ;;
  40. C )
  41. CHDIR="$OPTARG"
  42. ;;
  43. x )
  44. CMD="$OPTARG"
  45. ;;
  46. \? ) cat 1>&2 <<'USAGEEOF'
  47. Usage: sharxz [-dikp] [-I INSTALL_DIR] [-T TAR_OPTIONS] [-C CHRDIR] [-x CMD] INFILE
  48. -d DEGUG
  49. -i Interactive mode
  50. -k Keep Install Directory after exit
  51. -p Parse .sharxz contents (env, excluded)
  52. -P Add Parser to sharxz (for unpack settings, pass args after --)
  53. -T Options passed on to "tar c $TAR_OPTIONS"
  54. -I Preset Install Directory
  55. -C change directory to CHDIR, defaults to parent of INFILE
  56. -X execute CMD instead of INFILE after unpacking
  57. INFILE file to be executed on unpack.
  58. USAGEEOF
  59. exit 1
  60. ;;
  61. : )
  62. echo "Invalid option: $OPTARG requires an argument" 1>&2
  63. exit 1
  64. ;;
  65. esac
  66. done
  67. shift $((OPTIND -1))
  68. INFILE=$1
  69. shift 1
  70. [ "$1" == "--" ] && shift 1 && ARGS+=( "$@" ) && shift $#
  71. [ ! -z "$1" ] && OUTPUT="$1" && shift 1
  72. ARGS+=( "$@" ) && shift $#
  73. [ "$OUTPUT" == "-" ] && OUTPUT="/dev/stdout"
  74. [ "$CHDIR" == "" ] && [ -d "$INFILE" ] &&
  75. CHDIR="$INFILE"
  76. [ "$CHDIR" == "" ] &&
  77. CHDIR="$(dirname $INFILE)"
  78. [ ! -f "$INFILE" ] && [ ! -d "$CHDIR" ] &&
  79. echo "Error: INFILE: $INFILE is not a file, and CHDIR: $CHDIR is not a directory" 1>&2 && exit 1
  80. [ -z "$CMD" ] && [ ! -f "$INFILE" ] &&
  81. echo "Error: $INFILE is not a file, and no CMD was given" 1>&2 && exit 1
  82. [ -z "$CMD" ] &&
  83. CMD="./$(realpath "--relative-to=$CHDIR" "$INFILE")"
  84. $DEBUG && echo "CMD: $CMD" 1>&2
  85. {
  86. MYPWD="$PWD"
  87. cd "$CHDIR"
  88. $DEBUG && du -sh . 1>&2
  89. $PARSE && parse_options
  90. print_shar "$INFILE"
  91. cd "$MYPWD"
  92. } > "$OUTPUT"
  93. [[ "$OUTPUT" == "~/dev" ]] || chmod +x "$OUTPUT"
  94. }
  95. parse_options(){
  96. [ -f .sharxz/env ] && source .sharxz/env
  97. [ -f .sharxz/exclude ] && while read -r line; do
  98. local OPTION="$(trim "$line")"
  99. [ -z "$OPTION" ] && continue
  100. [[ $OPTION == \#* ]] && continue
  101. $DEBUG && echo "Excluding: $line" 1>&2
  102. TAR_OPTIONS+=( "--exclude=$OPTION" )
  103. done < .sharxz/exclude
  104. }
  105. print_shar(){
  106. printf "#!/usr/bin/env bash\n\nset -e\n"
  107. printf "DEBUG=$DEBUG\n"
  108. printf "KEEP=$KEEP\n"
  109. printf "INTERACTIVE=$INTERACTIVE\n"
  110. printf "INSTALL_DIR=\"$INSTALL_DIR\"\n\n"
  111. $ADD_PARSER && printf -- "$SHARPARSER\n"
  112. printf -- "$PREPSHAR\n\n"
  113. printf -- "$UNSHAR\n\n"
  114. $DEBUG && pwd 1>&2
  115. $DEBUG && set -x
  116. tar -c ${TAR_OPTIONS[@]} .| xz -c -3 -T 0 | base64 -w 0
  117. $DEBUG && set +x
  118. printf -- "\n$SETUPSHAR\n"
  119. printf -- "$CMD ${ARGS[@]}"' "$@"'"\n"
  120. printf -- "\n$CLEANUPSHAR\n"
  121. }
  122. trim() {
  123. local var="$*"
  124. # remove leading whitespace characters
  125. var="${var#"${var%%[![:space:]]*}"}"
  126. # remove trailing whitespace characters
  127. var="${var%"${var##*[![:space:]]}"}"
  128. printf '%s' "$var"
  129. }
  130. read -r -d '' SHARPARSER <<'SHARPARSEREOF'
  131. while getopts ":diI:-" opt; do
  132. case $opt in
  133. d )
  134. DEBUG=true
  135. ;;
  136. i )
  137. INTERACTIVE=true
  138. ;;
  139. I )
  140. INSTALL_DIR="$OPTARG"
  141. ;;
  142. - )
  143. break
  144. ;;
  145. \? ) cat 1>&2 <<'USAGEEOF'
  146. Usage: <sharxz> [-di] [-I INSTALL_DIR]
  147. -d DEGUG
  148. -i Interactive mode
  149. -I Preset Install Directory
  150. -- stop parsing and pass remaining arguments
  151. USAGEEOF
  152. exit 1
  153. ;;
  154. : )
  155. echo "Invalid option: $OPTARG requires an argument" 1>&2
  156. exit 1
  157. ;;
  158. esac
  159. done
  160. shift $((OPTIND -1))
  161. SHARPARSEREOF
  162. read -r -d '' PREPSHAR <<'PREPSHAREOF'
  163. REQUIREMENTS=(
  164. "xz"
  165. "tar"
  166. "base64"
  167. "mkfifo"
  168. "mktemp"
  169. )
  170. for REQ in "${REQUIREMENTS[@]}"; do
  171. [ -x "$(which $REQ 2> /dev/null)" ] || { echo "This sharxz needs $REQ in PATH." 1>&2 && exit 1; }
  172. done
  173. $DEBUG && set -x
  174. cleanup(){
  175. EXITCODE=$?
  176. rm -f "$FIFO_NAME"
  177. $KEEP || rm -r "$INSTALL_DIR"
  178. exit $EXITCODE
  179. }
  180. FIFO_NAME=$(mktemp -u).fifo
  181. mkfifo "$FIFO_NAME"
  182. [ -z "$INSTALL_DIR" ] && INSTALL_DIR="$(mktemp -d)"
  183. $INTERACTIVE && echo "INSTALL_DIR: [ $INSTALL_DIR ]" && read INSTALL_DIR
  184. [ -z "$INSTALL_DIR" ] && exit 1
  185. [ -d "$INSTALL_DIR" ] || mkdir -p "$INSTALL_DIR" || exit 1
  186. trap cleanup EXIT
  187. PREPSHAREOF
  188. read -r -d '' UNSHAR <<'UNSHAREOF'
  189. cat "$FIFO_NAME"|base64 -d |xz -d -T 0 |tar -xC "$INSTALL_DIR" &
  190. cat <<PAYLOADEOF > "$FIFO_NAME"
  191. UNSHAREOF
  192. read -r -d '' SETUPSHAR <<'SETUPSHAREOF'
  193. PAYLOADEOF
  194. wait
  195. rm "$FIFO_NAME"
  196. cd "$INSTALL_DIR"
  197. set +e
  198. $DEBUG && pwd 1>&2
  199. SETUPSHAREOF
  200. read -r -d '' CLEANUPSHAR <<'CLEANUPSHAREOF'
  201. CLEANUPSHAREOF
  202. [[ "${BASH_SOURCE[0]}" != "${0}" ]] && export -f sharxz || sharxz "$@"