Враховуючи шлях до файлу рядків, наприклад /foo/fizzbuzz.bar
, як я використовую bash, щоб витягти лише fizzbuzz
частину зазначеного рядка?
Враховуючи шлях до файлу рядків, наприклад /foo/fizzbuzz.bar
, як я використовую bash, щоб витягти лише fizzbuzz
частину зазначеного рядка?
Відповіді:
Ось як це зробити з операторами # і% в Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
також може бути ${x%.*}
видалити все після крапки або ${x%%.*}
видалити все після першої точки.
Приклад:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
Документацію можна знайти в посібнику Bash . Шукайте розділ узгодження частини ${parameter%word}
та ${parameter%%word}
контур.
подивіться на команду basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Використовуючи базове ім'я, я досяг цього:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Це не вимагає апріорних знань про розширення файлу і працює навіть у тому випадку, коли у вас є ім'я файлу, яке має в ньому ім’я файлів (перед його розширенням); вона вимагає програми basename
, але це є частиною GNU coreutils, тому вона повинна поставлятися з будь-яким дистрибутивом.
fname=`basename $file .$ext`
Чистий баш спосіб:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Ця функціональність пояснюється в man bash в розділі "Розширення параметрів". Небасових способів багато: awk, perl, sed і так далі.
EDIT: Працює з крапками у суфіксах файлів і не потрібно знати суфікс (розширення), але не працює з крапками в самому імені .
Функції базового імені та ім’я файлів - це те, що вам потрібно:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Має вихід:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
а не поточним постійним значенням (яке придушить кілька попереджень, будучи певним, що вони не містять пробілів / символів глобуса / тощо), і вирішіть його проблеми знахідки?
echo "basename: $(basename "$mystring")"
- таким чином, якщо після завершення mystring='/foo/*'
ви не *
заміните список файлів у поточному каталозі . basename
Використання basename
передбачає, що ви знаєте, що таке розширення файлу, чи не так?
І я вважаю, що різні пропозиції регулярних виразів не справляються з іменем файлу, що містить більше одного "."
Наступне, здається, справляється з подвійними крапками. О, і назви файлів, які містять самі "/" (лише для ударів)
Перефразовуючи Паскаля, "Вибачте, цей сценарій такий довгий. Я не встиг його скоротити"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
На додаток до відповідного синтаксису POSIX, який використовується у цій відповіді ,
basename string [suffix]
як і в
basename /foo/fizzbuzz.bar .bar
GNUbasename
підтримує інший синтаксис:
basename -s .bar /foo/fizzbuzz.bar
з тим же результатом. Різниця і перевага полягає в тому , що це -s
означає -a
, що підтримує кілька аргументів:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Це навіть можна зробити безпечним для імен файлів, розділивши вихідний сигнал з байтами NUL, використовуючи -z
параметр, наприклад, для цих файлів, що містять пробіли, нові рядки та символи глобу (цитується ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Читання в масив:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
потрібен Bash 4.4 або новіший. Для старих версій нам потрібно зробити цикл:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Якщо ви не можете використовувати базове ім'я, як пропонується в інших публікаціях, ви завжди можете використовувати sed. Ось (потворний) приклад. Він не найбільший, але він працює, витягуючи шуканий рядок і замінюючи вхід шуканим рядком.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Що отримає вам вихід
фізбуз
Остерігайтеся запропонованого рішення perl: воно видаляє все після першої крапки.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Якщо ви хочете зробити це за допомогою perl, це працює:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Але якщо ви використовуєте Bash, рішення з y=${x%.*}
(або basename "$x" .ext
якщо ви знаєте розширення) набагато простіші.
Базове ім'я це робить, видаляє шлях. Він також видалить суфікс, якщо він вказаний, і якщо він відповідає суфіксу файлу, але вам потрібно знати суфікс, який слід надати команді. В іншому випадку ви можете використовувати mv і розібратися, яке нове ім’я має бути іншим способом.
Поєднуючи відповідь із найвищим рейтингом та відповідь другого рейтингу, щоб отримати ім'я файлу без повного шляху:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
та${parameter%%word}
в розділі відповідності деталей.