'ls -1': как вывести список имен файлов без расширения


24

ls -1 перечисляет мои элементы так:

foo.png
bar.png
foobar.png
...

Я хочу, чтобы это перечислилось без .pngтак:

foo
bar
foobar
...

(каталог содержит только .pngфайлы)

Может кто-нибудь сказать мне, как использовать grepв этом случае?

Цель: у меня есть текстовый файл, где перечислены все имена без расширения. Я хочу сделать скрипт, который сравнивает текстовый файл с папкой, чтобы увидеть, какой файл отсутствует.


36
Вы хотите быть осторожным с такой просьбой. Linux не имеет расширений файлов. В Linux есть имена файлов, которые могут включать или не включать .в них. Хотя в соглашении говорится, что имена файлов .pngдолжны указываться в конце, нет причины, по которой у меня не может быть файла png с именем foo.zipor my.picture.20160518or just mypic.
hymie

2
@hymie Я знаю, но все мои элементы в этой папке названы в конце .png.
Колин

14
Что такое "расширение"? Это не часть имен файлов Unix; это перенос из VMS / NT / Windows, что угодно. И вы, ребята, тоже сойдите с моей лужайки. :)
mpez0

28
Давайте не будем преувеличивать это. ОС рассматривает расширения как просто часть имени файла, но на них обращает внимание множество программ unix, от компилятора до графического интерфейса. Концепция, безусловно, не чужды unix.
Алексис

1
Обычно рекомендуется избегать синтаксического анализа выходных данныхls и передавать по конвейеру выходные данные, lsи find, главным образом, из-за возможности использовать newlineсимвол `tab char в имени файла. Если имя файла The new art of working on .png\NEWLINE files and other formatsмного, предлагаемое решение создаст проблемы.
Хастур

Ответы:


41

Вам нужна только оболочка для этой работы.

POSIXly:

for f in *.png; do
    printf '%s\n' "${f%.png}"
done

С zsh:

print -rl -- *.png(:r)

4
Там нет необходимости printf; echo ${f%.png}будет достаточно.
Дэвид Конрад

11
@Conrad: использование echo не будет работать должным образом в некоторых случаях, если имя файла начинается с тире или содержит экранированные последовательности.
cuonglm

3
@DavidConrad: см. Также unix.stackexchange.com/a/65819/38906
cuonglm

35
ls -1 | sed -e 's/\.png$//'

Команда sedудаляет (то есть заменяет пустую строку) любую строку, .pngнайденную в конце имени файла.

Символ .экранируется \.так, что он интерпретируется sedкак буквальный .символ, а не как регулярное выражение .(что означает совпадение с любым символом). $Является якорным концом-линии, так что не соответствует .pngв середине файла.


4
Я думаю, что ОП хочет удалить любое расширение, но, вероятно, только «последнее». Так что, возможно, измените свой хороший ответ с помощью:sed 's/\.[^.]*$//'
Otheus

1
да, это регулярное выражение сработало бы в этом случае ... но если ОП хочет этого, они должны сказать об этом, вместо того, чтобы прямо сказать, что они "хотят, чтобы оно было внесено в список без .png"
cas

4
Это -1не обязательно, так как здесь по умолчанию.
Jlliagre

3
@jlliagre Я согласен с cas, что -1следует указать. Это только по умолчанию, когда труба включена, что является скрытым сюрпризом для некоторых. Таким образом, сделать это явно помогает пониманию. Я также делаю это в своих сценариях, чтобы знать, какой вывод я ожидаю.
Отей

1
Предупреждение В случае имени файла с ключом ( .png) перед символом новой строки вы удалите даже это, .pngа не только последнее. Лучше избегать передачи и разбора вывода ls, он часто оставляет за собой хорошо скрытые сюрпризы ... (несколько слов и ссылок больше в ответе).
Хастур

16

Если вы просто хотите использовать bash:

for i in *; do echo "${i%.png}"; done

Вы должны тянуться grepпри попытке найти совпадения, а не об удалении / замене того, что sedболее уместно:

find . -maxdepth 1 -name "*.png"  | sed 's/\.png$//'

Как только вы решите, что вам нужно создать несколько подкаталогов для наведения порядка в ваших файлах PNG, вы можете легко изменить это на:

find . -name "*.png"  | sed 's/\.png$//'

лс -1 | sed 's / .png //' прекрасно работает. Спасибо!
Колин

find Конвейер в sedраствор может представить некоторые проблемы , если вы нашли файл с ключом ( .png) как часть имени и непосредственно перед символом новой строки. Лучше избегать передачи и анализа выходных данных findили ls, это часто оставляет скрытые сюрпризы ... (некоторые слова и ссылки больше в ответе).
Хастур

Вероятно, заменить findна что-то, как echoв последнем примере. Непонятно, для каких целей они findслужат, и результаты зависят от структуры каталогов (то есть, если у вас есть каталог files.png)

@BroSlow Обновлен до чего-то более разумного.
Энтон

13

Я бы пошел basename(при условии реализации GNU):

basename --suffix=.png -- *.png

Обратите внимание, что если вы хотите использовать его в конвейере, может оказаться полезным использовать опцию базового имени -z(или --zero) GNU для создания вывода, разделенного NUL (вместо символа новой строки).
Тоби Спейт

11

Другой очень похожий ответ (я удивлен, что этот конкретный вариант еще не появился):

ls | sed -n 's/\.png$//p'
  • Вам не нужна эта -1опция ls, поскольку lsпредполагается, что если стандартный вывод не является терминалом (в данном случае это канал).
  • -nвариант sedозначает «не печатать строку по умолчанию»
  • /pвариант в конце замещения означает '... и распечатать эту строку , если замена была сделана.

Чистый эффект , который должен распечатать только те строки , которые заканчиваются .png, с .pngудалены. То есть это также способствует небольшому обобщению вопроса ОП, где каталог не содержит только .pngфайлы.

sed -nМетод часто оказывается полезным в тех случаях , когда вы могли бы использовать Grep + СЭД.


Мне нравится, как вы заботились, чтобы написать свой ответ. Это решение будет представлять проблемы с именами файлов, включая переводы строк , и не будет печатать первую часть имени. Еще больше, если он будет более неприятным с key ( .png) перед символом перевода строки: в этом случае вы напечатаете эту часть без png, не стирая только последнюю часть. Часто предлагается избегать анализа (и передачи) результатов, lsпотому что проблемы могут быть скрыты именно там, где вы не думаете ...
Хастур

2
@Hastur В принципе, вы правы, а на известной странице о том, что не разбирается, перечисляются дальнейшие проблемы (и решения) при обработке патологических имен файлов. Но лучший способ справиться с этим - избежать патологических имен файлов (дох!); и если вы не можете, или если вы должны быть стойкими против них, то используйте findили - возможно, лучше - используйте более мощный язык, чем shуправлять им (тот факт, что sh можно делать все, не означает, что это лучший выбор в каждый случай). Оболочка предназначена для удобства использования в первую очередь.
Норман Грей

В принципе, я согласен с юзабилити, но этот вариант не работает, если у вас есть имя файла с каждой новой строкой внутри. Это может легко произойти незамеченным, например, когда вы копируете и вставляете строку из pdf в GUI, так что вы думаете, что следует избегать только патологических имен файлов .
Хастур

Более того ИМХО С парсинга легко начать разбираться ls, но это грядущие проблемы. Часто мы создаем сценарии, которые будем использовать позже, когда мы уже забудем их предел ... (это человек, это обычно). Я предложил в findпример (с -execи без трубы) , даже если я считаю лучший вариант (потому что чистая раковина) ответить на один cuonglm в твердый и POSIX - совместимым.
Хастур

@Hastur: эти будущие проблемы возникнут в любом случае. Многие вещи в системе не устойчивы к файлам с символами новой строки. Например, попробуйте использовать locateили makeна них.
reinierpost

8

Для этого вы можете использовать только команды BASH (без каких-либо внешних инструментов).

for file in *; do echo "${file%.*}"; done 

Это полезно, когда у вас нет / usr / bin и хорошо работает с именами файлов, такими как this.is.image.png, и со всеми расширениями.


6

Не безопасно разбирать lsили передавать по трубам find[ 1 , 2 ]

Не безопасно анализировать (и передавать) выходные данные lsили find, главным образом потому, что в именах файлов можно найти необычные символы, такие как символ новой строки , вкладка ... Здесь будет работать чистый цикл оболочки [ cuonglm ] .
Даже findкоманда, не включенная в опцию, -execбудет работать:

find ./*.png  -exec  basename {} .png  \;

Обновления / Заметки : Вы можете использовать, find .чтобы искать даже скрытые файлы или find ./*.pngполучать только не скрытые. С ним find *.png -exec ...могут возникнуть проблемы, если в нем присутствовал файл с именем, .pngпотому что find получит его в качестве опции. Вы можете добавить, -maxdepth 0чтобы избежать спуска в каталогах, названных как Dir_01.png, или find ./*.png -prune -exec ...когда maxdepth не разрешен (спасибо Стефану). Если вы хотите избежать перечисления этих каталогов, вы должны добавить опцию -type f(которая также исключает другие типы нестандартных файлов). Посмотрите manна более полную панораму обо всех доступных опциях, и не забудьте проверить, когда они совместимы с POSIX, для лучшей переносимости.

Еще несколько слов

Например, может случиться так, что при копировании заголовка из документа и вставке в имя файла одна или несколько строк новой строки будут заканчиваться в самом имени файла. Мы можем быть настолько неудачливы, что заголовок может содержать даже ключ, который мы должны использовать непосредственно перед новой строкой:

The new art of working on .png
files and other formats.

Если вы хотите проверить, вы можете создать имена файлов, как это с помощью команд

touch "A file with two lines"$'\n'"and This is the second.png"
touch "The new art of working on .png"$'\n'"files and other formats.png"

Простое /bin/ls *pngбудет выводить ?вместо непечатных символов

A file with two lines?and This is the second.png
The new art of working on .png?files and other formats.png

Во всех случаях , в которых вы будете трубы на выходе из lsили findследующая команда не будет иметь никакого намека , чтобы понять , если нынешняя линия исходит от нового имени файла или если он следует символ новой строки символ в Прецедент имени файла . Противное имя действительно, но до сих пор правовые один.

Цикл оболочки с параметром-расширением оболочки ${parameter%word}, в обоих вариантах с printfили echoбудет работать [ cuonglm ], [ Anthon1 ] .

for f in *.png; do printf "%s\n" "${f%.png}" ; done

Со страницы руководства по расширению параметров оболочки [ 3 ]

$ {параметр% word}
$ {параметр %% word}

... результатом раскрытия является значение параметра с удаленным самым коротким шаблоном соответствия (регистр '%') или самым длинным шаблоном соответствия (регистр '%%').


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

1
Уважаемый @BroSlow, когда я написал ответ выше, я попробовал 13 (все) других вариантов, присутствующих в тот момент, с помощью командной строки в скрипте, запущенном как аргумент вызова оболочки. Пожалуйста, сделайте то же самое и скажите мне, ведут ли они себя так, как вы ожидаете. Я делал свои тесты с bash 4.3.11dash 0.5.7-4, zsh (при необходимости) 5.0.2. Не стесняйтесь читать этот пост, который добавляет что-то еще. Я согласен с примечанием о том, как обвязать вывод find, для этого я прямо предложил-exec , и я написал в заголовке. :-),
Хастур

Перечитайте вики еще раз. Я все еще думаю, что вам нужно использовать трубку в вашем примере, так как это то, что обсуждается здесь. И для большинства современных версий lsнет никакой проблемы, когда вывод передается по каналу или перенаправляется, но, как упоминалось в вики, он может работать не для всех. Большинство будет вставлять ?вместо специальных символов, только когда выходные данные отправляются на терминал. т.е. echo *.png | od -cи ls *.png | od -c. Проблема с новой строкой - это не проблема ls, это проблема с любой командой, которая не завершается нулем по обе стороны канала.

1
printf "${f%.png}\n"неправильно. Первый аргумент - это формат, вы не должны использовать переменные данные там. Может даже рассматриваться как уязвимость DoS (попробуйте, например, с %1000000000s.pngфайлом).
Стефан Шазелас

Вам понадобится find ./*.png -prune -exec...или у вас будут проблемы с именами файлов, начинающимися с -(и файлами типа directory, обратите внимание, что они -maxdepthне переносимы)
Стефан Шазелас

4

разве этого было недостаточно?

ls -1 | sed 's/\.png//g'

или вообще это

ls -1 | sed 's/\.[a-z]*//g'

удалит все расширения


Это было, но другие решения тоже работают.
Колин

Я хотел сказать, что ваш вопрос начался с ls -1, поэтому ls -1 должен это сделать. :)
Рохай Аббас

Это -1не обязательно, так как здесь по умолчанию.
Jlliagre

@Rohail Abbas Но не каждая система была установлена?
Colin

1
Действительно, но lsделает это в любом случае без этой опции, когда его вывод не является терминалом, как здесь.
Jlliagre

3

Используйте rev:

ls -1 | rev | cut -f 2- -d "." | rev

revпереворачивает все строки (строки); Вы режете все после первого "." и rev реверсирует остаток.

Если вы хотите grep«Алма»:

ls -1 | rev | cut -f 2- -d "." | rev | grep 'alma'

Это -1не обязательно, так как здесь по умолчанию.
Jlliagre

2
Это терпит неудачу на файле с именемMy.2016.Summer.Vacation.png
Дэвид Конрад

@DavidConrad мой плохой: / я исправилcut -f 2-
Том Солид

Теперь он работает с этим файлом, но еще не с файлом с .pngновой строкой сразу после ... Рекомендуется избегать разбора, lsпотому что он любит хорошо скрывать сюрпризы ... :-)
Hastur

2

Если бы я знал, что в каталоге есть только файлы с расширением .png, я бы просто запустил: ls | awk -F. '{print $1}'

Это вернет первое «поле» для всего, где есть имя файла .extension.

Пример:

[rsingh@rule51 TESTDIR]$ ls
10.png  1.png  2.png  3.png  4.png  5.png  6.png  7.png  8.png  9.png

[rsingh@rule51 TESTDIR]$ ls | awk -F. '{print $1}'
10
1
2
3
4
5
6
7
8
9

К сожалению, это не удастся для всех имен файлов с более чем одним ., как Image.1.pngи даже для имен с не хорошими именами , со специальными символами внутри. как символ новой строки , или тот , который вы будете использовать в качестве (вход) разделитель записей в awk, RS. Рекомендуется избегать разбора lsвыходных данных, потому что он любит скрывать проблемы, которые возникнут, когда вы этого не ожидаете. Вы можете прочитать больше в этих ссылках 1 или 2 . Кстати, хорошая идея использовать awk ... Я привел несколько примеров в одном ответе.
Хастур

Правда, однако, учитывая образец, предоставленный Колином, он будет работать просто отлично. Чтобы это работало в предложенном вами случае, я, вероятно, изменил бы его на: [rsingh @ rule51 TESTDIR] $ ls | sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename Не пытаясь быть сложным, но учитывая потребность Колина, я не уверен в чем проблема будет разбор ls.
rsingh

извините ... я только что понял, что не отображал каталог с файлами до того, как sed изменил вывод 'ls' [rsingh @ rule51 TESTDIR] $ ls 10.png 2.png 4.png 6.png 8.png harry.the.bunny.png 1.png 3.png 5.png 7.png 9.png whats.a.png.filename.png [rsingh @ rule51 TESTDIR] $ ls | sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename
rsingh

Note1 вам нужно бежать .в \.внутри sed -e 's/\.png$//', но так будет ответ только что написал. :-( note2 вы можете попытаться использовать awkчто-то вроде ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... но у вас всегда будет проблема, из-за которой awk не может знать, идет ли следующая строка или нет новой строки в имени файла. Я попытался сказать лучше в своем ответе .
Hastur

Спасибо Хастур, я пропустил это :). Кроме того, я отказался от использования awk в пользу sed в этом случае.
rsingh

2

в соответствии с вашим комментарием «У меня есть текстовый файл, в котором перечислены все имена без расширения. Я хочу сделать PHP-скрипт, который сравнивает текстовый файл с папкой, чтобы увидеть, какой файл отсутствует»:

for file in $(cat yourlist) ; do
  [ -f "${file}.png" ] || {
    echo "$file : listed in yourlist, but missing in the directory"
  }
done
#assumes that filenames have no space...
# otherwise use instead:
#  while IFS= read file ; do ...(same inner loop as above)... ; done < yourlist

и наоборот:

for file in *.png ; do
  grep "^${file%.png}$" yourlist >/dev/null || {
    echo "$file: present in the directory but not listed in yourlist"
  }
done
#I assume there are no spaces/tabs/? before/after names in 'yourlist'. Change the script accordingly if there are some (or sanitize the list)

1

ls -l | sed 's/\.png$//'

Это самый точный метод, как подчеркивает @roaima. Без уцелевших \.pngфайлы с именем a_png.pngбудут перечислены как: a_.


использование ls -l, как вы делаете, дает подробности файла, это не то, о чем спрашивал ОП.
Энтон

1

Простая строка оболочки (ksh, bash или zsh; не тире):

set -- *.png; printf '%s\n' "${@%.png}"

Простая функция (без расширения):

ne(){ set -- *.png; printf '%s\n' "${@%.png}"; }

Или функция, которая удаляет любое данное расширение (png по умолчанию):

ne(){ ext=${1:-png}; set -- *."$ext"; printf '%s\n' "${@%.${ext}}"; }

Использовать как:

ne jpg

Если выходные данные являются звездочкой *, файл с таким расширением не существует.


1

Вы можете попробовать следующую ленту awk, если у вашего суператора есть "." и так как все ваши файлы будут иметь name.png, вы напечатаете первый столбец:
ls | awk -F"." '{print $1}'


-1

Если у вас есть доступ к sed, это лучше, так как он удалит последнее расширение файла, независимо от того, что это (png, jpg, tiff и т. Д.)

ls | sed -e 's/\..*$//'

7
Разрывы для имен файлов, как this.is.a.dotty.txt. Попробуй s/\.[^.]*$//вместо этого.
Ройма
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.