Biorąc pod uwagę ścieżkę do pliku łańcuchowego, na przykład /foo/fizzbuzz.bar
, w jaki sposób użyłbym bash do wyodrębnienia tylko fizzbuzz
części tego łańcucha?
Biorąc pod uwagę ścieżkę do pliku łańcuchowego, na przykład /foo/fizzbuzz.bar
, w jaki sposób użyłbym bash do wyodrębnienia tylko fizzbuzz
części tego łańcucha?
Odpowiedzi:
Oto jak to zrobić za pomocą operatorów # i% w Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
może być również ${x%.*}
usunięcie wszystkiego po kropce lub ${x%%.*}
usunięcie wszystkiego po pierwszej kropce.
Przykład:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
Dokumentację można znaleźć w instrukcji Bash . Szukaj ${parameter%word}
i ${parameter%%word}
dopasowuj sekcję dopasowywania porcji.
spójrz na polecenie basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Używając basename, wykorzystałem następujące do osiągnięcia tego:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Nie wymaga to a priori znajomości rozszerzenia pliku i działa nawet wtedy, gdy nazwa pliku zawiera kropki w nazwie pliku (przed rozszerzeniem); wymaga to basename
jednak programu , ale jest to część jądra GNU, więc powinno być dostarczane z dowolną dystrybucją.
fname=`basename $file .$ext`
Pure Bash Way:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Ta funkcjonalność została wyjaśniona w podręczniku man bash w „Rozszerzeniu parametrów”. Istnieje wiele sposobów na bash: awk, perl, sed i tak dalej.
EDYCJA: Działa z kropkami w sufiksach plików i nie musi znać sufiksu (rozszerzenia), ale nie działa z kropkami w samej nazwie .
Funkcje basename i dirname są tym, czego szukasz:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Ma moc wyjściową:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
zamiast aktualnej wartości stałej (które tłumią kilka ostrzeżeń, będąc pewny, aby nie zawierać spacji / znaków glob / etc), oraz adres zagadnień IT znajdzie?
echo "basename: $(basename "$mystring")"
- w ten sposób, jeśli po zakończeniu mystring='/foo/*'
nie zostanie *
zastąpiona lista plików w bieżącym katalogu . basename
Używanie basename
zakłada, że wiesz, jakie jest rozszerzenie pliku, prawda?
I wierzę, że różne propozycje wyrażeń regularnych nie radzą sobie z nazwą pliku zawierającą więcej niż jeden „.”
Poniższe wydaje się radzić sobie z podwójnymi kropkami. Aha, i nazwy plików, które same zawierają „/” (tylko dla kopnięć)
Parafrazując Pascala, „Przepraszam, że ten skrypt jest tak długi. Nie miałem czasu, aby go skrócić”
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Oprócz składni zgodnej z POSIX zastosowanej w tej odpowiedzi ,
basename string [suffix]
jak w
basename /foo/fizzbuzz.bar .bar
GNUbasename
obsługuje inną składnię:
basename -s .bar /foo/fizzbuzz.bar
z tym samym wynikiem. Różnica i zaletą jest to -s
implikuje -a
, który obsługuje wiele argumentów:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Można to nawet zabezpieczyć pod nazwą pliku, oddzielając dane wyjściowe bajtami NUL za pomocą -z
opcji, na przykład dla plików zawierających spacje, znaki nowej linii i znaki globalne (cytowane przez ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Odczytywanie do tablicy:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
wymaga wersji Bash 4.4 lub nowszej. W przypadku starszych wersji musimy zapętlić:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Jeśli nie możesz użyć basename jak sugerowano w innych postach, zawsze możesz użyć sed. Oto (brzydki) przykład. Nie jest najlepszy, ale działa poprzez wyodrębnienie poszukiwanego ciągu i zastąpienie wejścia żądanym ciągiem.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Co da ci wynik
fizzbuzz
Uwaga na sugerowane rozwiązanie perla: usuwa wszystko po pierwszej kropce.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Jeśli chcesz to zrobić za pomocą Perla, działa to:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Ale jeśli używasz Basha, rozwiązania z y=${x%.*}
(lub basename "$x" .ext
jeśli znasz rozszerzenie) są znacznie prostsze.
Basename to robi, usuwa ścieżkę. Usunie również sufiks, jeśli jest podany i jeśli pasuje do sufiksu pliku, ale trzeba znać przyrostek, aby podać polecenie. W przeciwnym razie możesz użyć mv i dowiedzieć się, jaka powinna być nowa nazwa.
Łącząc najwyżej ocenianą odpowiedź z drugą najwyżej ocenianą odpowiedzią, aby uzyskać nazwę pliku bez pełnej ścieżki:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
i${parameter%%word}
w części dotyczącej dopasowywania części końcowej.