Дело не в эффективности, а в правильности. basenameиспользует новые строки для разграничения имен файлов, которые он печатает. В обычном случае, когда вы передаете только одно имя файла, он добавляет завершающий символ новой строки в свой вывод. Поскольку имена файлов могут содержать символы новой строки, это затрудняет правильную обработку этих имен файлов.
Это осложняется еще и тем , что люди , как правило , используют basenameтак: "$(basename "$file")". Это делает вещи еще более сложными, потому что $(command)убирает все завершающие символы новой строки command. Рассмотрим маловероятный случай, который $fileзаканчивается новой строкой. Затем basenameдобавит дополнительную новую строку, но "$(basename "$file")"удалит обе строки, оставив вас с неправильным именем файла.
Другая проблема в basenameтом, что если $fileначинается с -(тире или минус), это будет интерпретироваться как опция. Это легко исправить:$(basename -- "$file")
Надежный способ использования basenameзаключается в следующем:
# A file with three trailing newlines.
file=$'/tmp/evil\n\n\n'
# Add an 'x' so we can tell where $file's newlines end and basename's begin.
file_x="$(basename -- "$file"; printf x)"
# Strip off two trailing characters: the 'x' added by us and the newline added by basename.
base="${file_x%??}"
Альтернативой является использование ${file##*/}, которое проще, но имеет свои собственные ошибки. В частности, это неправильно в тех случаях, когда $fileесть /или foo/.