find . -iname "*.sh" | sed 's/[.]sh$//'
./backup/samukano/ex04/who_am_i
./backup/samukano/ex05/people
./days/day01/samukano/ex01/print_groups
./days/day01/samukano/ex02/find_sh
./samukano/ex04/who_am_i
./samukano/ex05/people

А мне нужно просто

who_am_i
people
...
people

2 ответа2

1
find . -iname "*.sh" -exec basename {} \; | sed s/\.sh//g 

Утилита basename удаляет всю информацию каталога из пути - относительную или абсолютную.

Утилита dirname при необходимости делает противоположное.

0

ТЛ; др

Портативный, не прочный:

find . -type f \( -name "*.sh" -o -name "*.Sh" -o -name "*.sH" -o -name "*.SH" \) |
sed -e 's|^.*/||' -e 's/[.][sS][hH]$//'

Надежный, для анализа в виде строк с нулевым символом в конце:

find . -type f -iname "*.sh" -printf "%f\0" | sed -z 's/[.][sS][hH]$//'

Внутренне устойчивый с возможно неоднозначным выводом, для просмотра:

find . -type f -iname "*.sh" -printf "%f\0" | sed -z 's/[.][sS][hH]$/\n/'


Полный ответ

Есть много способов, некоторые более надежные, чем другие. Кроме того, инструменты, которые вы, возможно, захотите использовать, имеют перекрывающиеся возможности, хотя многие из них не POSIX, поэтому вы можете иметь или не иметь требуемые опции.

Кроме того, любой каталог с именем файла может содержать символы новой строки и другие странные символы. По этой причине, чтобы анализировать имена файлов надежным способом, вы хотели бы рассматривать их как строки с нулевым символом в конце.

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

Инструменты обычно поддерживают строки с нулевым символом в конце с расширениями, отличными от POSIX.


Например, чтобы избавиться от этих полных путей, которые являются основной проблемой, вы можете использовать:

  • find . -printf "%f" , но ваша find может поддерживать или не поддерживать -printf ;
  • или basename ;
  • или даже sed .

Для .sh вы можете использовать:

  • basename path_to_process .sh , но он чувствителен к регистру, и я думаю, что вы использовали -iname по причине (примечание -iname не требуется POSIX);
  • или sed (как вы сделали), который можно сделать без учета регистра.

Этот анализ не является исчерпывающим.


Самая надежная версия без учета регистра возвращает строки с нулевым символом в конце. Это делает вывод полностью надежным для дальнейшего анализа:

find . -type f -iname "*.sh" -printf "%f\0" | sed -z 's/[.][sS][hH]$//'

То же самое, возвращая читабельный и, возможно, неоднозначный вывод:

find . -type f -iname "*.sh" -printf "%f\0" | sed -z 's/[.][sS][hH]$/\n/'

Неоднозначность проявляется так:

foo
bar

Были ли два сценария: foo.sh и bar.sh? или только один foo(newline here)bar.sh?

Это станет еще более запутанным, если вы не сможете использовать sed -z и должны предоставить входные данные для sed виде строк, заканчивающихся символом новой строки. В этом случае один файл foo.sh(newline here)bar.sh также будет генерировать вышеуказанный вывод.

Что если ваш sed поддерживает -z но find не поддерживает -printf? Может быть, ваше basename поддерживает -z . Это должно быть полностью надежно:

find . -type f -iname "*.sh" -exec basename -z -a {} + | sed -z 's/[.][sS][hH]$//'

или без поддержки -a:

find . -type f -iname "*.sh" -exec basename -z {} \; | sed -z 's/[.][sS][hH]$//'

Я вижу, что ваша find поддерживает -iname , хотя может и не быть. Что если это не так? Довольно наивный, но рабочий подход:

find . -type f \( -name "*.sh" -o -name "*.Sh" -o -name "*.sH" -o -name "*.SH" \) ...

Как видите, все зависит от доступных опций.


Наконец, давайте предположим, что ваши пути используют безопасный набор печатных символов (без перевода строки и т.д.). Эта команда должна быть переносимой и достаточно хорошей:

find . -type f \( -name "*.sh" -o -name "*.Sh" -o -name "*.sH" -o -name "*.SH" \) |
sed -e 's|^.*/||' -e 's/[.][sS][hH]$//'

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