Wyświetl wszystkie pliki binarne z $ PATH

30

Czy istnieje jedna linijka, która wyświetli wszystkie pliki wykonywalne z $ PATH w bash.

jcubic
źródło

Odpowiedzi:

38

To nie jest odpowiedź, ale pokazuje binarne polecenie, które można uruchomić

compgen -c

(zakładając bash)

Inne przydatne polecenia

compgen -a # will list all the aliases you could run.
compgen -b # will list all the built-ins you could run.
compgen -k # will list all the keywords you could run.
compgen -A function # will list all the functions you could run.
compgen -A function -abck # will list all the above in one go.
Rahul Patil
źródło
1
Dobrze wiedzieć, że to polecenie, a do wykonania potrzebuję plików wykonywalnych. Może zamiast tego używam tego polecenia.
jcubic
Zauważ, że zawiera także wbudowane funkcje, funkcje, słowa kluczowe (jak in, {...) i aliasy.
Stéphane Chazelas
Sir, zaktualizowałem .. Zapisałem w swoim projekcie, dawno temu znalazłem na tej stronie ..
Rahul Patil
@ jcubic, powłoki już to robią w celu wykonania polecenia Dlaczego to zrobić ręcznie?
vonbrand
@ vonbrand Pracuję nad powłoką w javascript / php i wykonuję powłokę w trybie nieinteraktywnym.
jcubic
15

Z zsh:

whence -pm '*'

Lub:

print -rl -- $commands

(zwróć uwagę, że w przypadku poleceń, które występują w więcej niż jednym składniku $PATH, będą one wyświetlać tylko pierwszy).

Jeśli chcesz, aby polecenia nie zawierały pełnych ścieżek i zostały posortowane dla zachowania dokładności:

print -rl -- ${(ko)commands}

(to znaczy pobierz klucze z tej tablicy asocjacyjnej zamiast wartości).

Stéphane Chazelas
źródło
Nie wspomniałem, że używam bash.
jcubic
5

W dowolnej powłoce POSIX, bez użycia zewnętrznego polecenia (przy założeniu, że printfjest wbudowane, jeśli nie cofnij się echo), z wyjątkiem końcowego sortowania i zakładając, że żadna nazwa pliku wykonywalnego nie zawiera nowej linii:

{ set -f; IFS=:; for d in $PATH; do set +f; [ -n "$d" ] || d=.; for f in "$d"/.[!.]* "$d"/..?* "$d"/*; do [ -f "$f" ] && [ -x "$f" ] && printf '%s\n' "${x##*/}"; done; done; } | sort

Jeśli nie masz pustego komponentu $PATH(użyj .zamiast niego) ani komponentów rozpoczynających się od -, ani symboli wieloznacznych ani \[?*w komponentach PATH, ani w nazwach plików wykonywalnych, i żadnych plików wykonywalnych zaczynających się od ., możesz uprościć to w celu:

{ IFS=:; for d in $PATH; do for f in $d/*; do [ -f $f ] && [ -x $f ] && echo ${x##*/}; done; done; } | sort

Korzystanie z POSIX findi sed:

{ IFS=:; set -f; find -H $PATH -prune -type f -perm -100 -print; } | sed 's!.*/!!' | sort

Jeśli chcesz podać w ścieżce rzadki plik niewykonywalny lub plik nieregularny, istnieje o wiele prostszy sposób:

{ IFS=:; ls -H $PATH; } | sort

Pomija to pliki kropkowe; jeśli ich potrzebujesz, dodaj -Aflagę ls, jeśli masz ją lub jeśli chcesz trzymać się POSIX:ls -aH $PATH | grep -Fxv -e . -e ..

Istnieją prostsze rozwiązania w bash i w zsh .

Gilles „SO- przestań być zły”
źródło
Zakłada się, że $PATHjest ustawiony i nie zawiera pustych komponentów i że komponenty nie wyglądają jak znajdź predykaty (lub opcje ls). Niektóre z nich będą również ignorować pliki kropek.
Stéphane Chazelas
@StephaneChazelas Tak, ok. Poza pustymi komponentami należy to do kategorii „nie rób tego” - ŚCIEŻKA jest pod twoją kontrolą.
Gilles „SO- przestań być zły”
Nadal nie działa, jeśli pusty element jest ostatni (jak zwykle jest). (oprócz emulacji yashi zshemulacji).
Stéphane Chazelas
W swoim finddrugim. -prunezapobiegnie wyświetlaniu katalogów. Prawdopodobnie chcesz -Lzamiast -Hjak chcesz dołączyć dowiązania symboliczne (wspólne dla plików wykonywalnych). -perm -100nie daje gwarancji, że plik będzie wykonywalny przez Ciebie (i może (mało prawdopodobne) wykluczyć pliki wykonywalne).
Stéphane Chazelas
4

Wymyśliłem to:

IFS=':';for i in $PATH; do test -d "$i" && find "$i" -maxdepth 1 -executable -type f -exec basename {} \;; done

EDYCJA : Wydaje się, że jest to jedyne polecenie, które nie wyzwala alertu SELinux podczas odczytywania niektórych plików w katalogu bin przez użytkownika apache.

jcubic
źródło
5
Dlaczego for? IFS=:; find $PATH -maxdepth 1 -executable -type f -printf '%f\n'
manatwork
@manatwork czy będzie działać na nieistniejących ścieżkach?
jcubic
@manatwork nie wiedział, że możesz to zrobić. muszę przeczytać więcej o IFS.
jcubic
3
Zakłada się, że $PATHjest ustawiony i nie zawiera symboli wieloznacznych oraz nie zawiera pustych składników. To zakłada również implementację GNU find.
Stéphane Chazelas
2
Ponieważ -type fzamiast (specyficzne dla GNU) -xtype f, to również pominie dowiązania symboliczne. To również nie wyświetli zawartości $PATHskładników, które są dowiązaniami symbolicznymi.
Stéphane Chazelas
3

Co powiesz na to

find ${PATH//:/ } -maxdepth 1 -executable

Podstawienie łańcucha jest używane z Bash.


źródło
3
Przy założeniu, że $PATHjest ustawiony, nie zawiera symboli wieloznacznych ani pustych znaków, nie zawiera pustych składników. To zakłada także GNU find. Zauważ, że ${var//x/y}jest to kshskładnia (obsługiwana także przez zsh i bash). Ściśle mówiąc, zakłada to również, że składniki $ PATH również nie są findpredykatami.
Stéphane Chazelas
1
Zakłada to również, że $PATHkomponenty nie są dowiązaniami symbolicznymi.
Stéphane Chazelas
@StephaneChazelas: Dzięki! Innymi słowy, zwykły przypadek.
Ustawienie IFS=:jest bardziej niezawodne niż wykonywanie tej zamiany. Ścieżki ze spacjami nie są tak rzadkie w systemie Windows. Dowiązania symboliczne są dość powszechne, ale można je łatwo rozwiązać -H.
Gilles „SO- przestań być zły”
@Gilles: oczywiście. jednak nie widzę żadnego uzasadnionego przypadku użycia dla tego pytania, dlatego nie ma potrzeby udzielania odpowiedzi kuloodpornej.
1

Jeśli możesz uruchomić Pythona w swojej powłoce, możesz również użyć następującej (absurdalnie długiej) jednowarstwowej:

python -c 'import os;import sys;output = lambda(x) : sys.stdout.write(x + "\n"); paths = os.environ["PATH"].split(":") ; listdir = lambda(p) : os.listdir(p) if os.path.isdir(p) else [ ] ; isfile = lambda(x) : True if os.path.isfile(os.path.join(x[0],x[1])) else False ; isexe = lambda(x) : True if os.access(os.path.join(x[0],x[1]), os.X_OK) else False ; map(output,[ os.path.join(p,f) for p in paths for f in listdir(p) if isfile((p,f)) and isexe((p,f)) ])'

Było to głównie zabawne ćwiczenie dla mnie, aby sprawdzić, czy można to zrobić za pomocą jednego wiersza kodu Pythona bez uciekania się do korzystania z funkcji „exec”. W bardziej czytelnej formie i z pewnymi komentarzami kod wygląda następująco:

import os
import sys

# This is just to have a function to output something on the screen.
# I'm using python 2.7 in which 'print' is not a function and cannot
# be used in the 'map' function.
output = lambda(x) : sys.stdout.write(x + "\n")

# Get a list of the components in the PATH environment variable. Will
# abort the program is PATH doesn't exist
paths = os.environ["PATH"].split(":")

# os.listdir raises an error is something is not a path so I'm creating
# a small function that only executes it if 'p' is a directory
listdir = lambda(p) : os.listdir(p) if os.path.isdir(p) else [ ]

# Checks if the path specified by x[0] and x[1] is a file
isfile = lambda(x) : True if os.path.isfile(os.path.join(x[0],x[1])) else False

# Checks if the path specified by x[0] and x[1] has the executable flag set
isexe = lambda(x) : True if os.access(os.path.join(x[0],x[1]), os.X_OK) else False

# Here, I'm using a list comprehension to build a list of all executable files
# in the PATH, and abusing the map function to write every name in the resulting
# list to the screen.
map(output, [ os.path.join(p,f) for p in paths for f in listdir(p) if isfile((p,f)) and isexe((p,f)) ])
brm
źródło
0
#!/usr/bin/env python
import os
from os.path import expanduser, isdir, join, pathsep

def list_executables():
    paths = os.environ["PATH"].split(pathsep)
    executables = []
    for path in filter(isdir, paths):
        for file_ in os.listdir(path):
            if os.access(join(path, file_), os.X_OK):
                executables.append(file_)
    return executables
Steven
źródło