Czy można odwoływać się do indeksów w $@
? Nie mogę znaleźć żadnego odniesienia do użycia, takiego jak nigdzie indziej w wiki GrayCata , a Advanced Scripting Guide i inni przypisują to do innej zmiennej przed jej zmodyfikowaniem.
$ echo ${@[0]}
-bash: ${@[0]}: bad substitution
Celem jest OSUSZANIE : Pierwszy argument jest używany do jednej rzeczy, a reszta do czegoś innego, i chciałbym uniknąć duplikowania albo kodu do normalizacji, $@
tablicy, albo do stworzenia oddzielnej funkcji do tego (chociaż w tym przypadku zaznacz, że jest to prawdopodobnie najłatwiejsze wyjście).
Wyjaśnienie: celem było zmodyfikowanie wartości zmiennej długości, $@
aby ułatwić debugowanie kodu . Obecna wersja jest dla mnie trochę zbyt zepsuta, chociaż działa nawet na dziwne ścieżki
$'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'
Aktualizacja : Wygląda na to, że nie jest to możliwe. Kod używa teraz zarówno duplikacji kodu, jak i danych, ale przynajmniej działa:
path_common()
{
# Get the deepest common path.
local common_path="$(echo -n "${1:-}x" | tr -s '/')"
common_path="${common_path%x}"
shift # $1 is obviously part of $1
local path
while [ -n "${1+defined}" ]
do
path="$(echo -n "${1}x" | tr -s '/')"
path="${path%x}"
if [[ "${path%/}/" = "${common_path%/}/"* ]]
then
shift
else
new_common_path="${common_path%/*}"
[ "$new_common_path" = "$common_path" ] && return 1 # Dead end
common_path="$new_common_path"
fi
done
printf %s "$common_path"
}
Bounty trafia do każdego, kto może pozbyć się duplikacji kodu, aby zwinąć duplikaty ukośników lub duplikaty danych do przechowywania, $1
a także inne parametry, lub oba, jednocześnie zachowując odpowiedni rozmiar kodu i pomyślnie przechodząc wszystkie testy jednostkowe:
test "$(path_common /a/b/c/d /a/b/e/f; echo x)" = /a/bx
test "$(path_common /long/names/foo /long/names/bar; echo x)" = /long/namesx
test "$(path_common / /a/b/c; echo x)" = /x
test "$(path_common a/b/c/d a/b/e/f ; echo x)" = a/bx
test "$(path_common ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
test "$(path_common $'\n/\n/\n' $'\n/\n'; echo x)" = $'\n/\n'x
test "$(path_common --/-- --; echo x)" = '--x'
test "$(path_common '' ''; echo x)" = x
test "$(path_common /foo/bar ''; echo x)" = x
test "$(path_common /foo /fo; echo x)" = x
test "$(path_common $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n' $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'; echo x)" = $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'x
test "$(path_common /foo/bar //foo//bar//baz; echo x)" = /foo/barx
test "$(path_common foo foo; echo x)" = foox
test "$(path_common /fo /foo; echo x)" = x
Odpowiedzi:
POSIX
Aby znormalizować ukośniki we wszystkich parametrach, użyję obrotowej sztuczki argumentów: odsuń
$1
, przekształć ją i umieść wynik na końcu listy parametrów. Jeśli robisz to tyle razy, ile jest parametrów, przekształcasz wszystkie parametry i odzyskujesz je w kolejności.W drugiej części kodu zmieniłem twoją logikę, aby była mniej myląca: zewnętrzna pętla iteruje parametry, a wewnętrzna pętla iteruje komponenty ścieżki.
for x; do … done
iteruje parametry pozycyjne, jest to wygodny idiom. Używam zgodnego z POSIX sposobu dopasowania łańcucha do wzorca:case
konstrukcja.Testowane z myślnikiem 0.5.5.1, pdksh 5.2.14, bash 3.2.39, bash 4.1.5, ksh 93s +, zsh 4.3.10.
Uwaga dodatkowa: wydaje się, że w wersji 4.1.5 bash występuje błąd (nie w wersji 3.2): jeśli wzór jest taki
"${common_path%/}"/*
, jeden z testów kończy się niepowodzeniem.bash, ksh
Jeśli jesteś w bash (lub ksh), możesz użyć tablic - nie rozumiem, dlaczego wydajesz się ograniczać do parametrów pozycyjnych. Oto wersja wykorzystująca tablicę. Muszę przyznać, że nie jest to szczególnie wyraźne niż wersja POSIX, ale pozwala uniknąć początkowego przetasowania n ^ 2.
W części normalizującej ukośnik używam konstruktu
${foo//PATTERN/REPLACEMENT}
konstrukcyjnego ksh93, aby zastąpić wszystkie wystąpieniaPATTERN
in$foo
przezREPLACEMENT
. Wzór ma+(\/)
pasować do jednego lub więcej ukośników; pod bash,shopt -s extglob
musi obowiązywać (równoważnie, zacznij bash zbash -O extglob
). Konstruktset ${!a[@]}
ustawia parametry pozycyjne na listę indeksów tablicya
. Zapewnia to wygodny sposób na iterację elementów tablicy.W drugiej części mam taką samą logikę pętli jak wersja POSIX. Tym razem mogę użyć,
[[ … ]]
ponieważ wszystkie ukierunkowane tu pociski obsługują to.Testowane z bash 3.2.39, bash 4.1.5, ksh 93s +.
zsh
Niestety, zsh nie ma opcji,
${!array[@]}
aby wykonać wersję ksh93 taką, jaka jest. Na szczęście Zsh ma dwie funkcje, dzięki którym pierwsza część jest bardzo prosta. Możesz indeksować parametry pozycyjne tak, jakby były@
tablicą, więc nie ma potrzeby używania tablicy pośredniej. A zsh ma konstrukcję iteracji tablicy :"${(@)array//PATTERN/REPLACEMENT}"
wykonuje kolejno zamianę wzoru na każdym elemencie tablicy i ocenia tablicę wyników (myląco potrzebujesz podwójnych cudzysłowów, mimo że wynik jest wieloma słowami; jest to uogólnienie"$@"
). Druga część jest zasadniczo niezmieniona.Przypadki testowe
Moje rozwiązania są minimalnie testowane i komentowane. Zmieniłem składnię twoich przypadków testowych, aby parsować pod powłokami, które nie mają,
$'…'
i zgłaszać awarie w wygodniejszy sposób.źródło
Dlaczego nie użyjesz 1 $, 2 $ .. 9 $, {10}, {11} .. i tak dalej? Jest jeszcze bardziej SUCHY niż to, co próbujesz zrobić :)
Więcej na temat relacji między liczbą $ a $ @:
$ @ można traktować jako skrót dla „wszystkich elementów tablicy zawierającej wszystkie argumenty”
$ @ Jest więc skrótem $ {args [@]} (args tutaj to tablica „wirtualna” zawierająca wszystkie argumenty - nie jest to prawdziwa zmienna, pamiętajcie)
1 $ to $ {args [1]}, 2 $ to $ {args [2]} i tak dalej.
Kiedy klikniesz [9], użyj nawiasu klamrowego: $ {10} to $ {args [10]}, $ {11} to $ {args [11]} i tak dalej.
Pośrednio użyj argumentu wiersza poleceń
Przykład:
źródło
${args[$i]}
.Myślę, że chcesz
shift
źródło
Nie do końca wiem, dlaczego nie używasz 1 USD 2 USD itp., Ale .. Może to odpowiadać Twoim potrzebom.
wynik
set
działa na wszystko, co następuje po nim, aby utworzyć 1 $, 2 $ .. itd. To oczywiście zastąpi oryginalne wartości, więc pamiętaj o tym.źródło
eval
niektórzy uważają go zaevil
... (może to z powodu pisowni :) ... Jeślieval
„zły”, to czy $ {! Var} jest równie „zły”? ... Dla mnie jest to tylko część języka i przydatna część, ale zdecydowanie wolę $ {!Uwaga: Obsługuję spacje w nazwach plików.
Dodałem przypadek testowy dla nazw plików ze spacjami i naprawiłem 2 testy, w których brakowało wiodącego /
źródło