View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0000276 | file | General | public | 2021-07-26 15:24 | 2024-04-06 13:55 |
| Reporter | abathur | Assigned To | christos | ||
| Priority | normal | Severity | minor | Reproducibility | always |
| Status | resolved | Resolution | reopened | ||
| Platform | intel/x86_64 | OS | macOS | OS Version | 10.15 |
| Product Version | 5.40 | ||||
| Fixed in Version | 5.46 | ||||
| Summary | 0000276: between 5.37 and 5.39, file starts identifying bin/sh script with patched shebang as awk/perl | ||||
| Description | I noticed a patched copy of esh 0.1.1 getting identified as an "awk or perl script" by newer versions of file (confirmed I see this in file 5.39 from nixpkgs and file 5.40 from homebrew). I've attached an unpatched copy named `esh` and a patched copy named `nix_esh`, but I assume the salient part is the fact that it has had its shebang patched from /bin/sh to /nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh Output here is captured on macOS, but I've confirmed the same behavior with file 5.39 in Linux (NixOS). | ||||
| Steps To Reproduce | # system/macOS file $ file --version file-5.37 magic file from /usr/share/file/magic # unpatched /bin/sh shebang $ file esh esh: POSIX shell script text executable, ASCII text # shebang patched by Nix to /nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh $ file nix_esh nix_esh: a /nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh script text executable, ASCII text # file from nixpkgs $ file --version file-5.39 magic file from /nix/store/77p3lid93i5xjgdi9vkj3zqcpf2zddlw-file-5.39/share/misc/magic # unpatched /bin/sh shebang $ file esh esh: POSIX shell script, ASCII text executable # shebang patched by Nix to /nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh $ file nix_esh nix_esh: awk or perl script, ASCII text # file from homebrew $ file --version file-5.40 magic file from /usr/local/Cellar/libmagic/5.40/share/misc/magic # unpatched /bin/sh shebang $ file esh esh: POSIX shell script, ASCII text executable # shebang patched by Nix to /nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh $ file nix_esh nix_esh: awk or perl script, ASCII text | ||||
| Tags | No tags attached. | ||||
|
|
esh (4,302 bytes)
#!/bin/sh
# vim: set ts=4:
#---help---
# Usage:
# esh [options] [--] INPUT [VARIABLE ...]
# esh <-h | -V>
#
# Process and evaluate an ESH template.
#
# Arguments:
# INPUT Path of the template file or "-" to read from STDIN.
# VARIABLE Variable(s) specified as NAME=VALUE to pass into the template
# (the have higher priority than environment variables).
#
# Options:
# -d Don't evaluate template, just dump a shell script.
# -o FILE Output file or "-" for STDOUT. Defaults to "-".
# -s SHELL Command name or path of the shell to use for template
# evaluation. It must not contain spaces. Defaults to "/bin/sh".
# -h Show this help message and exit.
# -V Print version and exit.
#
# Environment Variables:
# ESH_AWK Command name of path of the awk program to use.
# It must not contain spaces. Defaults to "awk".
# ESH_SHELL Same as -s.
#
# Please report bugs at <https://github.com/jirutka/esh/issues>.
#---help---
set -eu
# Set pipefail if supported.
if ( set -o pipefail 2>/dev/null ); then
set -o pipefail
fi
readonly PROGNAME='esh'
readonly VERSION='0.1.1'
AWK_PROGRAM=$(cat <<'AWK'
function fputs(str) {
printf("%s", str)
}
function read(len, _str) {
if (len == "") {
_str = buff
buff = ""
} else if (len > 0) {
_str = substr(buff, 1, len)
buff = substr(buff, len + 1, length(buff))
}
return _str
}
function skip(len) {
buff = substr(buff, len + 1, length(buff))
}
function flush(len, _str) {
_str = read(len)
if (state == "TEXT") {
gsub("'", "'\\''", _str)
}
if (state != "COMMENT") {
fputs(_str)
}
}
BEGIN {
FS = ""
buff = ""
if (shell) {
print("#!" (shell ~ /\// ? shell : "/usr/bin/env " shell))
}
print("set -eu")
print("if ( set -o pipefail 2>/dev/null ); then set -o pipefail; fi")
print("__print() { printf '%s' \"$*\"; }")
print(vars)
fputs("\nprintf '%s' '")
state = "TEXT"
}
{
buff = $0
while (buff != "") {
print_nl = 1
if (state == "TEXT" && match(buff, /<%/)) {
flush(RSTART - 1) # print buff before "<%"
skip(2) # skip "<%"
flag = substr(buff, 1, 1)
if (flag != "%") {
fputs("'\n") # close text
}
if (flag == "%") { # <%%
skip(1)
fputs("<%")
} else if (flag == "=") { # <%=
skip(1)
fputs("__print ")
state = "TAG"
} else if (flag == "#") { # <%#
state = "COMMENT"
} else {
state = "TAG"
}
} else if (state != "TEXT" && match(buff, /%>/)) {
flag = RSTART > 1 ? substr(buff, RSTART - 1, 1) : ""
if (flag == "%") { # %%>
flush(RSTART - 2)
skip(1)
flush(2)
} else if (flag == "-") { # -%>
flush(RSTART - 2)
skip(3)
print_nl = 0
} else { # %>
flush(RSTART - 1)
skip(2)
}
if (flag != "%") {
fputs("\nprintf '%s' '")
state = "TEXT"
}
} else {
flush()
}
}
if (print_nl && state != "COMMENT") {
fputs("\n")
}
}
END {
if (state == "TEXT") {
fputs("'\n")
}
}
AWK
)
readonly AWK_PROGRAM
help() {
sed -En '/^#---help---/,/^#---help---/p' "$0" | sed -E 's/^# ?//; 1d;$d;'
exit ${1:-0}
}
convert() {
local input="$1"
local vars="$2"
local evaluate="${3:-yes}"
if [ "$evaluate" = yes ]; then
$ESH_AWK -v vars="$vars" -- "$AWK_PROGRAM" "$input" | $ESH_SHELL -s
else
$ESH_AWK -v vars="$vars" -v shell="$ESH_SHELL" -- "$AWK_PROGRAM" "$input"
fi
}
: ${ESH_AWK:="awk"}
: ${ESH_SHELL:="/bin/sh"}
EVALUATE='yes'
OUTPUT=''
while getopts 'dho:s:V' OPT; do
case "$OPT" in
d) EVALUATE=no;;
h) help 0;;
o) OUTPUT="$OPTARG";;
s) ESH_SHELL="$OPTARG";;
V) echo "$PROGNAME $VERSION"; exit 0;;
'?') help 1 >&2;;
esac
done
shift $(( OPTIND - 1 ))
[ $# -ge 1 ] || help 1 >&2
INPUT="$1"; shift
[ "$INPUT" != '-' ] || INPUT=''
# Validate arguments.
for arg in "$@"; do
case "$arg" in
*=*) ;;
*) echo "$PROGNAME: illegal argument: $arg" >&2; exit 1;;
esac
done
# Format variables into shell variable assignments.
vars=''; for item in "$@"; do
vars="$vars\n${item%%=*}='$(
printf %s "${item#*=}" | $ESH_AWK "{ gsub(/'/, \"'\\\\\\\''\"); print }"
)'"
done
if [ "${OUTPUT#-}" ]; then
tmpfile="$(mktemp)"
trap "rm -f '$tmpfile'" EXIT HUP INT TERM
convert "$INPUT" "$vars" "$EVALUATE" > "$tmpfile"
mv "$tmpfile" "$OUTPUT"
else
convert "$INPUT" "$vars" "$EVALUATE"
fi
nix_esh (4,710 bytes)
#!/nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh
# vim: set ts=4:
#---help---
# Usage:
# esh [options] [--] INPUT [VARIABLE ...]
# esh <-h | -V>
#
# Process and evaluate an ESH template.
#
# Arguments:
# INPUT Path of the template file or "-" to read from STDIN.
# VARIABLE Variable(s) specified as NAME=VALUE to pass into the template
# (the have higher priority than environment variables).
#
# Options:
# -d Don't evaluate template, just dump a shell script.
# -o FILE Output file or "-" for STDOUT. Defaults to "-".
# -s SHELL Command name or path of the shell to use for template
# evaluation. It must not contain spaces. Defaults to "/nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/bash".
# -h Show this help message and exit.
# -V Print version and exit.
#
# Environment Variables:
# ESH_AWK Command name of path of the awk program to use.
# It must not contain spaces. Defaults to "/nix/store/8kpxw5na07ggdl2bs8kiwysif7120r6g-gawk-5.1.0/bin/awk".
# ESH_SHELL Same as -s.
#
# Please report bugs at <https://github.com/jirutka/esh/issues>.
#---help---
set -eu
# Set pipefail if supported.
if ( set -o pipefail 2>/dev/null ); then
set -o pipefail
fi
readonly PROGNAME='esh'
readonly VERSION='0.1.1'
AWK_PROGRAM=$(cat <<'AWK'
function fputs(str) {
printf("%s", str)
}
function read(len, _str) {
if (len == "") {
_str = buff
buff = ""
} else if (len > 0) {
_str = substr(buff, 1, len)
buff = substr(buff, len + 1, length(buff))
}
return _str
}
function skip(len) {
buff = substr(buff, len + 1, length(buff))
}
function flush(len, _str) {
_str = read(len)
if (state == "TEXT") {
gsub("'", "'\\''", _str)
}
if (state != "COMMENT") {
fputs(_str)
}
}
BEGIN {
FS = ""
buff = ""
if (shell) {
print("#!" (shell ~ /\// ? shell : "/usr/bin/env " shell))
}
print("set -eu")
print("if ( set -o pipefail 2>/dev/null ); then set -o pipefail; fi")
print("__print() { printf '%s' \"$*\"; }")
print(vars)
fputs("\nprintf '%s' '")
state = "TEXT"
}
{
buff = $0
while (buff != "") {
print_nl = 1
if (state == "TEXT" && match(buff, /<%/)) {
flush(RSTART - 1) # print buff before "<%"
skip(2) # skip "<%"
flag = substr(buff, 1, 1)
if (flag != "%") {
fputs("'\n") # close text
}
if (flag == "%") { # <%%
skip(1)
fputs("<%")
} else if (flag == "=") { # <%=
skip(1)
fputs("__print ")
state = "TAG"
} else if (flag == "#") { # <%#
state = "COMMENT"
} else {
state = "TAG"
}
} else if (state != "TEXT" && match(buff, /%>/)) {
flag = RSTART > 1 ? substr(buff, RSTART - 1, 1) : ""
if (flag == "%") { # %%>
flush(RSTART - 2)
skip(1)
flush(2)
} else if (flag == "-") { # -%>
flush(RSTART - 2)
skip(3)
print_nl = 0
} else { # %>
flush(RSTART - 1)
skip(2)
}
if (flag != "%") {
fputs("\nprintf '%s' '")
state = "TEXT"
}
} else {
flush()
}
}
if (print_nl && state != "COMMENT") {
fputs("\n")
}
}
END {
if (state == "TEXT") {
fputs("'\n")
}
}
AWK
)
readonly AWK_PROGRAM
help() {
/nix/store/fnzsi837b2xqqsfiq7hb61v5xka98avl-gnused-4.8/bin/sed -En '/^#---help---/,/^#---help---/p' "$0" | /nix/store/fnzsi837b2xqqsfiq7hb61v5xka98avl-gnused-4.8/bin/sed -E 's/^# ?//; 1d;$d;'
exit ${1:-0}
}
convert() {
local input="$1"
local vars="$2"
local evaluate="${3:-yes}"
if [ "$evaluate" = yes ]; then
$ESH_AWK -v vars="$vars" -- "$AWK_PROGRAM" "$input" | $ESH_SHELL -s
else
$ESH_AWK -v vars="$vars" -v shell="$ESH_SHELL" -- "$AWK_PROGRAM" "$input"
fi
}
: ${ESH_AWK:="/nix/store/8kpxw5na07ggdl2bs8kiwysif7120r6g-gawk-5.1.0/bin/awk"}
: ${ESH_SHELL:="/nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/bash"}
EVALUATE='yes'
OUTPUT=''
while getopts 'dho:s:V' OPT; do
case "$OPT" in
d) EVALUATE=no;;
h) help 0;;
o) OUTPUT="$OPTARG";;
s) ESH_SHELL="$OPTARG";;
V) echo "$PROGNAME $VERSION"; exit 0;;
'?') help 1 >&2;;
esac
done
shift $(( OPTIND - 1 ))
[ $# -ge 1 ] || help 1 >&2
INPUT="$1"; shift
[ "$INPUT" != '-' ] || INPUT=''
# Validate arguments.
for arg in "$@"; do
case "$arg" in
*=*) ;;
*) echo "$PROGNAME: illegal argument: $arg" >&2; exit 1;;
esac
done
# Format variables into shell variable assignments.
vars=''; for item in "$@"; do
vars="$vars\n${item%%=*}='$(
printf %s "${item#*=}" | $ESH_AWK "{ gsub(/'/, \"'\\\\\\\''\"); print }"
)'"
done
if [ "${OUTPUT#-}" ]; then
tmpfile="$(mktemp)"
trap "rm -f '$tmpfile'" EXIT HUP INT TERM
convert "$INPUT" "$vars" "$EVALUATE" > "$tmpfile"
mv "$tmpfile" "$OUTPUT"
else
convert "$INPUT" "$vars" "$EVALUATE"
fi
|
|
|
With the HEAD of the file code this reports: $ ./file -m ../magic/magic.mgc ~/nix_esh /Users/christos/nix_esh: a /nix/store/pcjan45rssdn01cxx3sjg70avjg6c3ni-bash-4.4-p23/bin/sh script, ASCII text executable |
|
|
Thanks--I'll keep an eye out for the next release. (I do see the same after figuring out how to rebuild the Nix package from the latest commit on the GH mirror). |
|
|
Release has been out. |
|
|
This morning I noticed that this regressed at some point between 5.43 and 5.44: $ file --version file-5.44 ... $ file /nix/store/y2jm9585rj81y9ks04b7ky2631ahgv3s-esh-0.1.1/bin/esh /nix/store/y2jm9585rj81y9ks04b7ky2631ahgv3s-esh-0.1.1/bin/esh: awk or perl script, ASCII text $ file --version file-5.43 ... $ file /nix/store/y2jm9585rj81y9ks04b7ky2631ahgv3s-esh-0.1.1/bin/esh /nix/store/y2jm9585rj81y9ks04b7ky2631ahgv3s-esh-0.1.1/bin/esh: a /nix/store/7xmqfgfgipjypqprhz0xw6bd3jb58z3y-bash-5.2p26/bin/sh script, ASCII text executable |
|
|
Fixed again, thanks! |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2021-07-26 15:24 | abathur | New Issue | |
| 2021-07-26 15:24 | abathur | File Added: esh | |
| 2021-07-26 15:24 | abathur | File Added: nix_esh | |
| 2021-07-30 08:43 | christos | Assigned To | => christos |
| 2021-07-30 08:43 | christos | Status | new => assigned |
| 2021-07-30 08:44 | christos | Status | assigned => feedback |
| 2021-07-30 08:44 | christos | Note Added: 0003630 | |
| 2021-07-31 22:28 | abathur | Note Added: 0003633 | |
| 2021-07-31 22:28 | abathur | Status | feedback => assigned |
| 2021-10-28 15:33 | christos | Status | assigned => resolved |
| 2021-10-28 15:33 | christos | Resolution | open => fixed |
| 2021-10-28 15:33 | christos | Note Added: 0003654 | |
| 2024-04-04 14:22 | abathur | Status | resolved => feedback |
| 2024-04-04 14:22 | abathur | Resolution | fixed => reopened |
| 2024-04-04 14:22 | abathur | Note Added: 0004025 | |
| 2024-04-06 13:55 | christos | Status | feedback => resolved |
| 2024-04-06 13:55 | christos | Fixed in Version | => 5.46 |
| 2024-04-06 13:55 | christos | Note Added: 0004026 |