Я пытаюсь установить переменную в скрипте sh для последних 3 символов базового имени файла (под базовым именем я имею в виду без пути и без суффикса). Я преуспел в этом, но, просто из любопытства, мне интересно, есть ли более короткая, единственная команда, которую я могу использовать. Первоначально у меня был однострочник awk
, но это было довольно долго. В настоящее время у меня есть этот двухстрочный скрипт (при условии, что полное имя файла находится в $1
):
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
Так, например, «/path/to/somefile.txt» заканчивается на «ile» в $lastpart
.
Можно ли как-то объединить basename
и бит, чтобы разделить суффикс в одну команду, и есть ли способ отправить его tail
(или что-то еще, что я могу использовать) без использования канала? Суффикс неизвестен, поэтому я не могу использовать его в качестве параметра basename
.
На самом деле главная цель не столько в том, чтобы быть как можно короче, сколько в том, чтобы быть читабельным с первого взгляда. Фактический контекст всего этого - этот вопрос на Superuser , где я пытаюсь найти достаточно простой ответ.
источник
file.one.two.three
? Хотели бы выile
илиtwo
?two
будет работать; расширение на это было бы,.three
я думаю.Ответы:
Это типичная работа для
expr
:Если вы знаете, что имена ваших файлов имеют ожидаемый формат (содержит одну и только одну точку и не менее 3 символов перед точкой), это можно упростить до:
Обратите внимание, что статус выхода будет ненулевым, если совпадения нет, но также если совпадающая часть - это число, которое разрешается до 0. (например, для
a000.txt
илиa-00.txt
)С
zsh
:(
:t
для хвоста (базовое имя),:r
для отдыха (с удаленным расширением)).источник
expr
это еще один, с которым мне нужно ознакомиться. Мне действительно нравятсяzsh
решения в целом (я только что читал о его поддержке вложенных подстановок на левой стороне${}
вчерашнего дня и хотел, чтобыsh
было то же самое), это просто облом, который не всегда присутствует по умолчанию.expr
это не POSIX. Это несомненно. Это редко встроенный хотя.Это сначала удаляет последние три символа, а
$var
затем удаляет из$var
результатов этого удаления - который возвращает последние три символа$var
. Вот несколько примеров, более конкретно направленных на демонстрацию того, как вы можете сделать такую вещь:Вам не нужно распространять все это через множество команд. Вы можете сжать это:
Комбинирование
$IFS
сset
параметрами оболочки ting также может быть очень эффективным средством анализа и сверления переменных оболочки:Это даст вам только три символа, непосредственно предшествующих первому периоду после последнего
/
в$path
. Если вы хотите получить только первые три символа, непосредственно предшествующие последним.
в$path
(например, если есть возможность более одного.
в имени файла) :В обоих случаях вы можете сделать:
И...
... напечатает то, что следует за
.
Если вы не возражаете против использования внешней программы, вы можете сделать:
Если есть вероятность
\n
появления символа ewline в имени файла (неприменимо для собственных решений оболочки - они все равно это обрабатывают) :источник
$base
оттуда последние 3 символа , лучшее, что я мог сделать, - это три строкиname=${var##*/} ; base=${name%%.*} ; lastpart=${base#${base%???}}
. С положительной стороны это чистый bash, но он по-прежнему 3 строки. (В вашем примере «/tmp/file.txt» мне понадобится «ile», а не «file».) Я только что многому научился о замене параметров; Я понятия не имел, что это могло бы сделать это ... довольно удобно. Лично я нахожу это очень читабельным.%
вместо%%
удаления суффикс, и мне на самом деле не нужно обрезать путь, чтобы я мог получить более хорошую, две строкиnoextn=${var%.*} ; lastpart=${noextn#${noextn%???}}
.$IFS
в${noextn}
и вы не процитировать расширение. Итак, это безопаснее:lastpart=${noextn#"${noextn%???}"}
Если вы можете использовать
perl
:источник
perl -e 'shift =~ /(.{3})\.[^.]*$/ && print $1' $filename
. Дополнительноеbasename
будет необходимо, если имя файла может не содержать суффикса, но какой-то каталог в пути содержит.perl -e 'shift =~ m#(.{3})(?:\.[^./]*)?$# && print $1' $filename
sed
работает для этого:Или
Если ваш
sed
не поддерживает-r
, просто заменить экземпляры()
с\(
и\)
, а затем-r
не требуется.источник
Если Perl доступен, я считаю, что он может быть более читабельным, чем другие решения, особенно потому, что его язык регулярных выражений более выразителен и имеет
/x
модификатор, который позволяет писать более понятные регулярные выражения:Это ничего не печатает, если нет такого соответствия (если базовое имя не имеет расширения или если корень до расширения слишком короткий). В зависимости от ваших требований вы можете настроить регулярное выражение. Это регулярное выражение применяет ограничения:
Использование этого в подстановке команд обычно приводит к удалению слишком большого количества завершающих строк новой строки, что также влияет на ответ Стефана. С этим можно справиться в обоих случаях, но здесь немного проще:
источник
python2.7
источник
Я думаю, что эта функция bash, pathStr (), сделает то, что вы ищете.
Не требует awk, sed, grep, perl или expr. Он использует только встроенные команды Bash, поэтому он довольно быстрый.
Я также включил зависимые функции argsNumber и isOption, но их функции могут быть легко включены в pathStr.
Зависимая функция ifHelpShow не включена, так как она имеет множество зависимостей для вывода текста справки либо в командной строке терминала, либо в диалоговое окно графического интерфейса пользователя через YAD . Переданный ему текст справки включен для документации. Посоветуйте, хотите ли вы ifHelpShow и его иждивенцев.
РЕСУРСЫ
источник
bash
isms - на вид проще, чем это. Кроме того, что это${#@}
?$#
это синтаксис для количества аргументов, и он также используется в другом месте здесь.${#@}
в большинстве случаев не эквивалентен - в спецификации POSIX указаны результаты любых расширений параметров либо,$@
либо$*
, к сожалению, они не определены. Это может работать,bash
но это не надежная функция, я думаю, это то, что я пытаюсь сказать.,