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


24

ls -1 перераховує мої елементи так:

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

Я хочу, щоб це було перераховано без .pngподібного:

foo
bar
foobar
...

(Режим містить лише .pngфайли)

Хтось може сказати мені, як grepв цьому випадку користуватися?

Призначення: у мене є текстовий файл, де вказані всі імена без розширення. Я хочу зробити сценарій, який порівнює текстовий файл із папкою, щоб побачити, який файл відсутній.


36
Ви хочете бути обережними з таким запитом. У Linux немає розширень назви файлів. Linux має назви файлів, які можуть включати або не включати .в них. Хоча угода говорить називати файли з .pngврешті-решт, немає ніяких причин , чому я не можу мати PNG файл з ім'ям foo.zipабо my.picture.20160518або просто mypic.
гімі

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

14
Що таке "розширення"? Це не є частиною іменування файлів Unix; це передача від VMS / NT / Windows як завгодно. І ви, молодці, також зійдете з моєї галявини. :)
mpez0

28
Давайте не перебільшувати це. ОС розглядає розширення як просто частину назви файлу, але багато unix програм звертають на них увагу - від компілятора до GUI. Ця концепція, безумовно, не чужа для Unix.
alexis

1
Зазвичай пропонується уникати розбору результатівls та передачі виводу, lsі find, головним чином, через можливість newlineвведення файлу табуляції табулятора у назві файлу. Якщо ім'я файлу 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/\.[^.]*$//'
Отей,

1
так, це regexp спрацювало б у цьому випадку ... але якщо ОП цього хоче, вони повинні сказати так, а не конкретно говорити, що "хочу, щоб це було перелічено без .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 (замість нового рядка).
Toby Speight

11

Ще одна дуже схожа відповідь (я здивований, що цей варіант ще не з'явився):

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

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

Ця sed -nметодика часто корисна в тих випадках, коли в іншому випадку ви можете використовувати grep + sed.


Мені подобається, як піклувалися ти про написання своєї відповіді. Це рішення створить проблеми з іменами файлів, включаючи нові рядки , воно не надрукує першу частину назви. Навіть більше, якщо це більш неприємно з ключем ( .png) перед символом нового рядка: у такому випадку ви будете друкувати цю частину без png, не стираючи лише останню частину. Часто пропонується уникати розбору (і труби) результату, lsтому що проблеми можна приховати саме там, де ви не замислюєтесь про ...
Гастур,

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

Я в принципі погоджуюся щодо зручності використання, але цей варіант не вдається, коли у вас є ім'я файлу з кожним новим рядком всередині. Це може бути легко непоміченим, наприклад, коли ви копіюєте та вставляєте рядок із pdf у графічний інтерфейс, тому ви думаєте лише уникнути патологічних імен .
Гастур

Більше того, IMHO легко почати розбирати 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 заборонено (дякую Stéphane). Якщо ви хочете уникати переліку цих каталогів, вам слід додати опцію -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.11, тире 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...або у вас виникнуть проблеми з іменами файлів, починаючи з -(і файлів типу каталогу, зверніть увагу, що -maxdepthце не портативний)
Stéphane Chazelas

4

хіба цього мало?

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

або взагалі це

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

видалить усі розширення


Було, але інші рішення теж працюють.
Колін

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

Це -1не обов'язково, оскільки тут за замовчуванням.
jlliagre

@Rohail Abbas Але чи не в кожній системі встановлено sed?
Колін

1
Дійсно, але lsчи все-таки без цього варіанту, коли його вихід не є терміналом, як це відбувається у цьому випадку.
jlliagre

3

Використання rev:

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

revперевертає всі рядки (рядки); ви вирізаєте все після першого "." і rev повертає назад залишок.

Якщо ви хочете grep"alma":

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

Це -1не обов'язково, оскільки тут за замовчуванням.
jlliagre

2
Це не вдається в файлі з назвоюMy.2016.Summer.Vacation.png
Девід Конрад

@DavidConrad мій поганий: / Я виправивcut -f 2-
Том Солід

Тепер він працює з цим файлом, але ще не з файлом з .pngновим рядком відразу після ... Пропонується уникати розбору, lsоскільки він любить добре приховувати сюрпризи ... :-)
Гастур,

2

Якби я знав, що в каталозі є лише файли з розширенням .png, я би просто запустив: ls | awk -F. '{print $1}'

Це поверне перше "поле" для будь-якого місця, де є filename.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 не може знати, чи надходить рядок, чи йде новий рядок всередині імені файлу. Я намагався сказати краще у своїй відповіді .
Гастур

Дякую Гастур, я це пропустив :). Крім того, я відмовився від використання 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; not dash):

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

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


-1

Якщо у вас є доступ до sed, це краще, оскільки воно зніме останнє розширення файлу, незалежно від того, яке воно є (png, jpg, tiff тощо).

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

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