Próbuję ustawić zmienną w skrypcie sh na 3 ostatnie znaki podstawowej nazwy pliku (pod nazwą podstawową mam na myśli bez ścieżki i sufiksu). Udało mi się to zrobić, ale z czystej ciekawości zastanawiam się, czy mogę użyć krótszego pojedynczego polecenia. Początkowo miałam jedną wkładkę awk
, ale była raczej długa. Obecnie mam ten dwuwierszowy skrypt (zakładając, że jest pełna nazwa pliku $1
):
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
Na przykład „/path/to/somefile.txt” kończy się na „ile” w $lastpart
.
Czy mogę jakoś połączyć basename
i nieco rozebrać sufiks w jednym poleceniu i czy istnieje sposób na wysłanie go do tail
(lub czegoś innego, z czego mogę skorzystać) bez użycia potoku? Przyrostek jest nieznany, więc nie mogę oprzeć go na parametrze basename
.
Głównym celem nie jest bycie tak krótkim, jak to tylko możliwe, aby być jak najbardziej czytelnym na pierwszy rzut oka. Rzeczywistym kontekstem tego wszystkiego jest pytanie Superuser , w którym staram się znaleźć dość prostą odpowiedź.
źródło
file.one.two.three
? Chceszile
czytwo
?two
działałby; Wydaje mi się, że to rozszerzenie.three
.Odpowiedzi:
To typowa praca dla
expr
:Jeśli wiesz, że nazwy plików mają oczekiwany format (zawiera jedną i tylko jedną kropkę oraz co najmniej 3 znaki przed kropką), można to uprościć w celu:
Pamiętaj, że stan wyjścia będzie różny od zera, jeśli nie ma dopasowania, ale także jeśli dopasowana część jest liczbą, która jest rozwiązywana do 0. (jak dla
a000.txt
luba-00.txt
)Z
zsh
:(
:t
dla ogona (basename),:r
dla odpoczynku (z usuniętym przedłużeniem)).źródło
expr
to kolejny, z którym muszę się zapoznać. I naprawdę jakzsh
rozwiązania w ogóle (ja po prostu czytać o jego wsparcie dla zagnieżdżonych substytucji na lewym boku${}
wczoraj też i pragnącsh
miał takie same), to po prostu porażka, że nie zawsze jest obecny domyślnie.expr
to nie POSIX. To z pewnością jest. Jednak rzadko jest wbudowany.To najpierw usuwa ostatnie trzy znaki,
$var
a następnie usuwa z$var
wyników tego usunięcia - co zwraca ostatnie trzy znaki$var
. Oto kilka przykładów, których celem jest pokazanie, jak możesz zrobić coś takiego:Nie musisz rozpowszechniać tego wszystkiego za pomocą tak wielu poleceń. Możesz to skompaktować:
Łączenie
$IFS
zset
parametrami powłoki ting może być również bardzo skutecznym sposobem analizowania i drążenia zmiennych zmiennych powłoki:Dzięki temu otrzymasz tylko trzy postacie bezpośrednio poprzedzające pierwszy okres po ostatnim
/
w$path
. Jeśli chcesz pobrać tylko trzy pierwsze znaki bezpośrednio poprzedzające ostatnie.
w$path
(na przykład, jeśli istnieje możliwość użycia więcej niż jednego.
w nazwie pliku) :W obu przypadkach możesz zrobić:
I...
... wydrukuje to, co następuje
.
Jeśli nie masz nic przeciwko korzystaniu z zewnętrznego programu, możesz:
Jeśli
\n
w nazwie pliku istnieje szansa na znak ewline (nie dotyczy rodzimych rozwiązań powłoki - wszystkie one i tak sobie radzą) :źródło
$base
stamtąd ostatnie 3 znaki , najlepsze, co mogłem zrobić, to trzywierszname=${var##*/} ; base=${name%%.*} ; lastpart=${base#${base%???}}
. Na plus to czysta walka, ale wciąż 3 linie. (W twoim przykładzie „/tmp/file.txt” potrzebowałbym „ile” zamiast „file”.) Po prostu wiele się nauczyłem o podstawianiu parametrów; Nie miałem pojęcia, że może to zrobić ... całkiem przydatne. Osobiście uważam to za bardzo czytelne.%
zamiast%%
usunąć sufiks i tak naprawdę nie muszę odcinać ścieżki, więc mogę uzyskać ładniejszą, dwie linienoextn=${var%.*} ; lastpart=${noextn#${noextn%???}}
.$IFS
w środku${noextn}
i nie zacytujesz rozszerzenia. Jest to więc bezpieczniejsze:lastpart=${noextn#"${noextn%???}"}
Jeśli możesz użyć
perl
:źródło
perl -e 'shift =~ /(.{3})\.[^.]*$/ && print $1' $filename
. Potrzebnebasename
będą dodatkowe , jeśli nazwa pliku może nie zawierać sufiksu, ale zawiera katalog na ścieżce.perl -e 'shift =~ m#(.{3})(?:\.[^./]*)?$# && print $1' $filename
sed
działa na to:Lub
Jeśli urządzenie
sed
nie obsługuje-r
, po prostu zastąpić wystąpień()
z\(
a\)
, a następnie-r
nie jest potrzebne.źródło
Jeśli perl jest dostępny, uważam, że może być bardziej czytelny niż inne rozwiązania, szczególnie dlatego, że jego język wyrażeń regularnych jest bardziej ekspresyjny i ma
/x
modyfikator, który pozwala na pisanie wyraźniejszych wyrażeń regularnych :Nie wypisuje nic, jeśli nie ma takiego dopasowania (jeśli basename nie ma rozszerzenia lub katalog główny przed rozszerzeniem jest zbyt krótki). W zależności od wymagań możesz dostosować wyrażenie regularne. Wyrażenie regularne wymusza ograniczenia:
Używanie tego w podstawianiu poleceń ma normalne problemy z usuwaniem zbyt wielu końcowych znaków nowej linii, problem, który również wpływa na odpowiedź Stéphane'a. W obu przypadkach można sobie z tym poradzić, ale tutaj jest trochę łatwiej:
źródło
Python2.7
źródło
Myślę, że ta funkcja bash, pathStr (), zrobi to, czego szukasz.
Nie wymaga awk, sed, grep, perl lub expr. Wykorzystuje tylko wbudowane bash, więc jest dość szybki.
Dołączyłem również zależne funkcje argsNumber i isOption, ale ich funkcje można łatwo włączyć do pathStr.
Funkcja zależna ifHelpShow nie jest uwzględniona, ponieważ ma wiele zależności zależnych do wyprowadzania tekstu pomocy albo w linii poleceń terminala, albo w oknie dialogowym GUI przez YAD . Przekazany do niego tekst pomocy jest dołączony do dokumentacji. Poradę, jeśli chcesz, jeśli ifHelpShow i jego podopieczni.
ZASOBY
źródło
bash
ismów - pozornie prostsze niż to. Co to jest${#@}
?$#
jest składnią liczby argumentów i jest ona również używana gdzie indziej.${#@}
nie jest równoważny w większości przypadków - specyfikacja POSIX podaje wyniki dowolnych rozszerzeń parametrów na jednym z nich$@
lub$*
są niestety nieokreślone. Może to działać,bash
ale nie jest to niezawodna funkcja, myślę, że to, co próbuję powiedzieć.,