Дивна поведінка з командою `basename` UNIX


2

Я хочу використовувати basename щоб отримати основні імена для файлів, розділених символом нового рядка. Здається, працює, коли n & gt; 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

Відповіді:


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 отримує три аргументи, як це, він виводить базову назву кожного аргументу окремо. Сторінка для basename не документує цю поведінку, тому, якщо що-небудь, це помилка, що в цьому випадку не дає помилки.

Аналогічно ця команда:

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

еквівалентно

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

... але в цьому випадку basename Поведінка визначається і зовсім інша - вона бере базову назву першого аргументу, видаляє другий аргумент з її кінця (якщо він збігається) і друкує результат. "бар" не закінчується на "/ food / baz", тому він просто друкує "бар".

Також, echo -e дивно непереносимий (навіть між різними версіями OS X!). Нижче я використовував bash's $' ' Строковий формат, щоб отримати трансляційні послідовності.

Якщо потрібно надрукувати базові назви шляхів, розділених символом нового рядка, використовуйте цикл для запуску 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@^.*/@@'

Дякую. Я закінчився з використанням xargs -n 1 замість циклу.
Andy Carlson

2

basename має додатковий аргумент суфікс напр.

basename include/stdio.h .h

повертається "stdio"

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

Ви можете спробувати

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

які повинні повернутися "ba".


0

Не впевнений, що в оборотах OS, але можуть бути деякі різні basename utilties. Шахта (/ usr / bin / basename на Mac OS 10.12.6) дає помилку про те "-e" в підкоманді echo, але це може бути моя оболонка (tcsh).

Це працює для мене:

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

0

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

$ basename /foo/bar.c .c
bar

це може бути зручно при компіляції або обробці файлу. наприклад:

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

process ${SRC} > ${DEST}

(зауважте, що вищезгаданий фрагмент не буде працювати з файлом із забавним символом (пробіл, вкладка або новий рядок)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.