Ujrzeć! Wytrzymała przemysłowa 12-liniowa ... technicznie przenośna powłoka bash i zsh, która z oddaniem uwielbia wybrany przez Ciebie skrypt startowy ~/.bashrc
lub ~/.zshrc
startowy:
# void +path.append(str dirname, ...)
#
# Append each passed existing directory to the current user's ${PATH} in a
# safe manner silently ignoring:
#
# * Relative directories (i.e., *NOT* prefixed by the directory separator).
# * Duplicate directories (i.e., already listed in the current ${PATH}).
# * Nonextant directories.
+path.append() {
# For each passed dirname...
local dirname
for dirname; do
# Strip the trailing directory separator if any from this dirname,
# reducing this dirname to the canonical form expected by the
# test for uniqueness performed below.
dirname="${dirname%/}"
# If this dirname is either relative, duplicate, or nonextant, then
# silently ignore this dirname and continue to the next. Note that the
# extancy test is the least performant test and hence deferred.
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
# Else, this is an existing absolute unique dirname. In this case,
# append this dirname to the current ${PATH}.
PATH="${PATH}:${dirname}"
done
# Strip an erroneously leading delimiter from the current ${PATH} if any,
# a common edge case when the initial ${PATH} is the empty string.
PATH="${PATH#:}"
# Export the current ${PATH} to subprocesses. Although system-wide scripts
# already export the ${PATH} by default on most systems, "Bother free is
# the way to be."
export PATH
}
Przygotujcie się na natychmiastową chwałę. Następnie, zamiast robić to i życzyć sobie najlepszego:
export PATH=$PATH:~/opt/bin:~/the/black/goat/of/the/woods/with/a/thousand/young
Zrób to zamiast tego i zapewnij sobie to, co najlepsze, niezależnie od tego, czy naprawdę tego chcesz, czy nie:
+path.append ~/opt/bin ~/the/black/goat/of/the/woods/with/a/thousand/young
Bardzo dobrze, określ „Najlepsze”.
Bezpieczne dołączanie i dopływanie do prądu ${PATH}
nie jest banalną sprawą, na którą zwykle się składa. Choć wygodne i pozornie rozsądne, jednowarstwowe formy export PATH=$PATH:~/opt/bin
zachęcają do diabelskich komplikacji z:
Przypadkowe nazwy względne (np export PATH=$PATH:opt/bin
.). Podczas gdy bash
i zsh
po cichu akceptuję i najczęściej ignoruję względne nazwy w większości przypadków, względne nazwy poprzedzone jednym h
lub t
(i prawdopodobnie innymi nikczemnymi bohaterami) powodują, że oba wstydliwie okaleczają się jako przełomowe arcydzieło Masakiego Kobayashiego z 1962 r. Harakiri :
# Don't try this at home. You will feel great pain.
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:harakiri && echo $PATH
/usr/local/bin:/usr/bin:arakiri
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:tanuki/yokai && echo $PATH
binanuki/yokai # Congratulations. Your system is now face-up in the gutter.
Przypadkowo zduplikowane nazwy. Chociaż zduplikowane ${PATH}
nazwy są w dużej mierze nieszkodliwe, są również niepożądane, nieporęczne, nieco nieefektywne, utrudniają debuggowanie i promują zużycie dysku - coś w rodzaju takiej odpowiedzi. Podczas gdy dyski SSD w stylu NAND są ( oczywiście ) odporne na zużycie odczytu, dyski HDD nie są. Niepotrzebny dostęp do systemu plików przy każdej próbie polecenia oznacza niepotrzebne zużycie głowicy odczytu w tym samym tempie. Duplikaty są szczególnie nieprzyjemne, gdy wywołuje się zagnieżdżone skorupy w zagnieżdżonych podprocesach, w których to pozornie nieszkodliwe jednowarstwowe, jak export PATH=$PATH:~/wat
szybko, eksplodują w Siódmym Kręgu ${PATH}
Piekła PATH=/usr/local/bin:/usr/bin:/bin:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat
. Tylko Belzebubba może ci pomóc, jeśli do tego dodasz dodatkowe nazwy. (Nie pozwól, aby stało się to z twoimi cennymi dziećmi. )
- Przypadkowo brakuje nazwisk. Ponownie, chociaż brakujące
${PATH}
nazwy są w dużej mierze nieszkodliwe, zazwyczaj są one również niepożądane, nieporęczne, lekko nieefektywne, utrudniają debuggowanie i sprzyjają zużyciu dysku.
Ergo, przyjazna automatyzacja, taka jak funkcja powłoki zdefiniowana powyżej. Musimy uratować się od siebie.
Ale ... Dlaczego „+ path.append ()”? Dlaczego nie po prostu append_path ()?
Dla disambiguity (na przykład za pomocą poleceń zewnętrznych obecnych ${PATH}
lub na cały system funkcji powłoki zdefiniowane gdzie indziej), funkcje powłoki zdefiniowane przez użytkownika są idealnie przedrostkiem lub przyrostkiem unikalnych podciągów obsługiwanych przez bash
i zsh
a, które są zabronione w standardzie basenames poleceń - takich jak, na przykład, +
.
Hej. To działa. Nie osądzaj mnie.
Ale ... Dlaczego „+ path.append ()”? Dlaczego nie „+ path.prepend ()”?
Ponieważ ${PATH}
dopływ do prądu jest bezpieczniejszy niż dopływ do prądu ${PATH}
, wszystkie rzeczy są równe, którymi nigdy nie są. Zastępowanie poleceń systemowych za pomocą poleceń specyficznych dla użytkownika może być w najlepszym razie niehigieniczne, aw najgorszym stanie się szaleństwem. Na przykład w systemie Linux aplikacje podrzędne zwykle oczekują wariantów poleceń GNU coreutils , a nie niestandardowych niestandardowych pochodnych lub alternatyw.
To powiedziawszy, istnieją absolutnie uzasadnione przypadki użycia. Zdefiniowanie równoważnej +path.prepend()
funkcji jest banalne. Sans prolix mgławica, bo on i jej wspólny rozsądek:
+path.prepend() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
PATH="${dirname}:${PATH}"
done
PATH="${PATH%:}"
export PATH
}
Ale ... dlaczego nie Gilles?
Gilles ' Akceptowane odpowiedź gdzie indziej jest imponująco optymalna w ogólnym przypadku jako «shell agnostycznego idempotent append» . W przypadku wspólnego bash
i zsh
ze bez niepożądanych dowiązania jednak kara wydajność wymaga tego smuci się Ricer Gentoo we mnie. Nawet w obecności niepożądanych dowiązań symbolicznych można zastanawiać się, czy rozwidlenie jednej podpowłoki na add_to_PATH()
argument jest warte potencjalnego wstawienia duplikatów dowiązań symbolicznych.
W przypadku ścisłych przypadków użycia, wymagających wyeliminowania nawet duplikatów dowiązań symbolicznych, ten zsh
wariant specyficzny robi to za pomocą wydajnych wbudowanych elementów zamiast nieefektywnych rozwidleń:
+path.append() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname:A}:"* &&
-d "${dirname}" ]] || continue
PATH="${PATH}:${dirname}"
done
PATH="${PATH#:}"
export PATH
}
Uwaga *":${dirname:A}:"*
raczej niż *":${dirname}:"*
oryginał. :A
jest cudowny - zsh
niestety nieobecny pod większością innych powłok - w tym bash
. Cytując man zshexpn
:
Odp . : Zmień nazwę pliku na ścieżkę bezwzględną, podobnie jak a
modyfikator, a następnie przekaż wynik przez funkcję realpath(3)
biblioteki, aby rozwiązać dowiązania symboliczne. Uwaga: w systemach, które nie mają realpath(3)
funkcji biblioteki, dowiązania symboliczne nie są rozwiązywane, więc w tych systemach a
i A
są równoważne.
Żadnych dalszych pytań.
Nie ma za co. Ciesz się bezpiecznym ostrzałem. Zasługujesz teraz na to.