henry_flower: A melancholy wolf (Default)
henry_flower ([personal profile] henry_flower) wrote2021-10-29 09:03 am

which(1)

Почитавши про бурю ув склянці з утілітою which(1) ув деб'яні, пограв сам з собою ув code golf: на якій мові вийде написати найкоротший аналог which(1), який

  1. їсть декілька агрументів, e.g.

     my-which ls BOGUS cat
  2. має зупинитися після 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())

Чудовими _ = _ => _ && _( _ => (_, _), _ => { ... })(_) не насолоджуватися неможливо.

juan_gandhi: (Default)

[personal profile] juan_gandhi 2021-10-29 09:13 am (UTC)(link)

Сколько нового узнал! Дякую!!! (command -v - never heard before!)

[personal profile] dsfhjkl 2021-10-29 05:17 pm (UTC)(link)
Дякую

[personal profile] sassa_nf 2021-10-30 12:04 pm (UTC)(link)
звісно краще і без луципирового свата shift()
#!/usr/bin/env node
const fs = require('fs')
const dirs = (process.env.PATH || '').split(':')

const f = (e, cont) => dirs.map(d => d + '/' + e)
                       .reduce((p, d) => g => p(f => f ? g(f):
                                                         fs.access(d, fs.constants.X_OK, err => g(!err && d))),
                               f => f())(f => f ? (console.log(f), cont()):
                                                  (console.log(`${process.argv[1]}: ${e} not found in PATH`), process.exitCode = 1, cont()))

process.argv.slice(2).reduce((p, c) => g => p(_ => f(c, g)), f => f())(_ => _)

[personal profile] sassa_nf 2021-10-31 07:04 am (UTC)(link)
А! Та то я виправив (console.error(`${process.argv[1]}: ${e} not found in PATH`), process.exitCode = 1) на (console.log(`${process.argv[1]}: ${e} not found in PATH`), process.exitCode = 1, cont()), бо він якраз закінчував роботу, не перевіряючи далі.
Edited 2021-10-31 10:16 (UTC)