Почитавши про бурю ув склянці з утілітою which(1) ув деб'яні, пограв сам з собою ув code golf: на якій мові вийде написати найкоротший аналог which(1), який
їсть декілька агрументів, e.g.
my-which ls BOGUS cat
має зупинитися після 1го неіснуючого виконуваного файлу та повернути, у такому разі, exit code > 0.
Найкрихітніший результат вийшов, як завжди, на GNU Make:
#!/usr/bin/make -f
f = $(firstword $(wildcard $(addsuffix /$1,$(subst :, ,$(PATH)))))
%:;@echo $(or $(call f,$@),$(error $@ not found in PATH))
Працює так:
$ ./which.mk ls BOGUS cat
/usr/bin/ls
which.mk:3: *** BOGUS not found in PATH. Stop.
$ echo $?
2
На 2му місці Рубі:
#!/usr/bin/env ruby
def f e; (ENV['PATH'] || '').split(?:).filter{|d| File.executable?(d+'/'+e)}.map{|d| d+'/'+e}[0]; end
ARGV.each {|i| puts(f(i) || abort("#{$0}: #{i} not found in PATH")) }
Дуже посередній результат на sh:
#!/bin/sh
f() {
IFS=':'
for p in $PATH; do
[ -x "$p/$1" ] && { echo "$p/$1"; return; }
done
return 1
}
for i in "$@"; do
f "$i" || { echo "$0: $i not found in PATH" 1>&2; exit 1; }
done
А найогиднійший, як завжди, на ноуді (v17.0.1):
#!/usr/bin/env node
let {access} = require('fs/promises')
let afilter = async (arr, predicate) => { // та за шо
return (await Promise.allSettled(arr.map(predicate)))
.filter( v => v.status === 'fulfilled').map( v => v.value)
}
let f = e => afilter((process.env.PATH || '').split(':'), async p => {
await access(p+'/'+e, 1)
return p+'/'+e
})
async function main() {
let args = process.argv.slice(2).map( async p => {
return {exe: p, location: await f(p)}
})
for await (let r of args) {
if (!r.location.length) {
console.error(`${process.argv[1]}: ${r.exe} not found in PATH`)
process.exitCode = 1
break
}
console.log(r.location[0])
}
}
main()
Можна було 1 в 1 скопіювати версію на рубі, але я хотів уникнути ⓐ
try/catch (fs.accessSync()
кидає іксепшона) та ⓑ process.exit()
.
Upd: не витримав, переписав останній приклад традиційними колбеками, стилем також відомим як як діди:
#!/usr/bin/env node
let fs = require('fs')
let f = (ok, error) => {
let dirs = (process.env.PATH || '').split(':')
return function dive(e) {
let dir = dirs.shift() || error(e); if (!dir) return
let file = dir+'/'+e
fs.access(file, fs.constants.X_OK, err => err ? dive(e) : ok(file))
}
}
let args = process.argv.slice(2)
let main = exe => exe && f( e => (console.log(e), main(args.shift())), e => {
console.error(`${process.argv[1]}: ${e} not found in PATH`)
process.exitCode = 1
})(exe)
main(args.shift())
Чудовими _ = _ => _ && _( _ => (_, _), _ => { ... })(_) не насолоджуватися неможливо.