2

Я хочу использовать basename чтобы получить базовые имена для разделенного новой строкой списка путей к файлам. Кажется, работает, когда n> 2:

$ basename `echo -e '/foo/bar \n /food/baz \n /oof/rab'`
bar
baz
rab

Однако, когда n = 2, он выводит только первую строку.

$ basename `echo -e '/foo/bar \n /food/baz'`
bar

Почему он не печатал baz? Это ошибка в basename? Вот информация о моей платформе:

$ uname -a
Darwin MacBook-Pro-4.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun  4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64

4 ответа4

5

basename не принимает список путей (не говоря уже о том, что разделены символами новой строки), оно использует один путь (необязательно сопровождаемый суффиксом для удаления).

Кроме того, когда вы используете команду (например, echo) в обратных галочках, оболочка принимает вывод этой команды, разделяет ее на "слова", разделенные пробелами (обычно пробелы, табуляции и новые строки, но вы можете изменить их с помощью $IFS), расширяет любой он находит подстановочные знаки и передает результат всего этого другой команде (в данном случае basename). В результате, новые строки в выводе echo обрабатываются как просто больше пробелов между словами, а не передаются в basename вообще. Итак, эта команда:

basename `echo -e '/foo/bar \n /food/baz \n /oof/rab'`

эквивалентно:

basename "/foo/bar" "/food/baz" "/oof/rab"

... и, очевидно, когда basename получает три аргумента, как это, он печатает базовое имя каждого аргумента отдельно. Страница man для basename не документирует это поведение, поэтому в любом случае это ошибка, которая не выдает ошибку в этом случае.

Аналогично, эта команда:

basename `echo -e '/foo/bar \n /food/baz'`

эквивалентно

basename "/foo/bar" "/food/baz"

... но в этом случае поведение basename определено и совершенно иное - оно берет базовое имя первого аргумента, удаляет второй аргумент с его конца (если он совпадает) и печатает результат. "bar" не заканчивается на «/food/baz», поэтому он просто печатает "bar".

Кроме того, echo -e странно непереносима (даже между различными версиями OS X!). Ниже, я использовал Баш в $' ' строки , чтобы избежать последовательностей переведены.

Если вы хотите напечатать базовые имена списка путей, разделенных новой строкой, используйте цикл для запуска basename для каждого отдельно:

while read -r filepath; do
    basename "$filepath"
done <<< $'/foo/bar \n /food/baz'

Или просто используйте sed для удаления до первой буквы "/" в каждой строке:

echo $'/foo/bar \n /food/baz \n /oof/rab' | sed 's@^.*/@@'
2

basename имеет необязательный суффикс аргумента, например

basename include/stdio.h .h

возвращает "stdio"

Ваш второй аргумент "/food/baz" разделенный пробелами " \n " рассматривался как суффикс. Поскольку он не совпадает, он возвращает только "bar" .

Ты можешь попробовать

basename `echo -e '/foo/bar \n r'`

который должен вернуть "ba" .

0

в дополнение к ответу @ Gordon, basename (и его dirname друга) иногда используются для удаления суффиксов.

$ basename /foo/bar.c .c
bar

это может быть удобно при компиляции или обработке файла. например:

SRC=$1
DEST=$(dirname $SRC)/$(basename $SRC .txt).csv

process ${SRC} > ${DEST}

(обратите внимание, что приведенный выше фрагмент не будет работать с файлом со смешным символом (пробел, табуляция или перевод строки)

0

Не уверен насчет версий ОС, но могут быть и другие утилиты с базовым именем. Мой (/usr/bin/basename в Mac OS 10.12.6) выдает ошибку об этом "-e" в подкоманде echo, но это может быть моя оболочка (tcsh).

Это работает для меня:

basename /foo/bar /food/baz /oof/rab

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .