LINUX.ORG.RU

Вопрос по shell

 , ,


0

1

Есть директория с файлами, в именах которых есть пробелы. Нужно применить к каждому из них какое-то действие. Для этого я использую for:

for i in `ls`; do

Но вот беда! for вместо того, чтобы i присваивать имя одного файла, присваивает слова из этого имени по порядку. (т.е. вместо применения операции к файлу «a b c.mp4», получается применение к трем файлам: «a», «b» и «c.mp4»)
Как сделать так, чтобы переменной присваивались именно имена файлов, а не их отдельные части.

Как сделать так, чтобы переменной присваивались именно имена файлов, а не их отдельные части.

способ №1

for f in *; do echo $f; done

способ №2 (быстрее и надёжнее)

find -exec echo {} +

emulek
()
Ответ на: комментарий от Gotf

А теперь вместо echo сделай file, пожалуйста. Или любую другую команду работающую с файлами, а не со строками.

И, да, ты не заметил что тебе вывело строку с \n ?

alozovskoy ★★★★★
()
Ответ на: комментарий от Gotf

'a b c

d e f' почему кавычек у тебя не 4, а 2? Ведь в каждой итерации печатается 2 кавычки. У тебя тут один «файл», одна итерация, которая выводит одну строку.

попробуй например:

$ for f in "`ls`"; do stat "$f"; done

или у тебя такие шутки?

emulek
()
Ответ на: комментарий от alozovskoy

Да, и правда. На самом деле я обычно использую, например,

foreach regular-file (*(.)) some-command $regular-file; end
или find, когда количество объектов слишком велико для глоббинга.

Gotf ★★★
()
Ответ на: комментарий от emulek

или у тебя такие шутки?

Нет, я просто ступил немного.

Gotf ★★★
()
Ответ на: комментарий от sdio

ls -1 | xargs -L1 -I{} action '{}'

а потом скажешь: «какое же говно ваш лялех»?

emulek
()

Большое спасибо.

qzxcvbnm
() автор топика
Ответ на: комментарий от root22

-1 в данном случае можно не указывать (он включается по умолчанию, когда ls пишет в pipe).

php-coder ★★★★★
()
Ответ на: комментарий от NeXTSTEP

Решение — использовать лаконичные конструкции типа find -mindepth 1 -maxdepth 1 -print0 | xargs -0 foo.

на кой ляд тут конвейер с xargs? Иди читай про GNU Findutils, это всё меняется одним плюсиком «+» в конце команды.

И да, про mindepth и maxdepth ТС не говорил. Ты уверен, что ему это нужно?

emulek
()
Ответ на: комментарий от emulek

это всё меняется одним плюсиком «+» в конце команды

Буду знать.

И да, про mindepth и maxdepth ТС не говорил. Ты уверен, что ему это нужно?

Если не задавать maxdepth, то find будет спускаться в поддиректории, а если не задавать mindepth, то find выдаст .. И то и другое не нужно ТС, раз он `ls` использует.

NeXTSTEP ★★
()
Ответ на: комментарий от NeXTSTEP

Если не задавать maxdepth, то find будет спускаться в поддиректории, а если не задавать mindepth, то find выдаст .. И то и другое не нужно ТС, раз он `ls` использует.

он использует `ls` для поиска файлов не по этому, а потому, что не осилил findutils. Я так вангую, что ему надо -type f, ибо он не считает директории файлами. Это в общем случае. В частном, скорее всего ТСу надо не просто «все», а «все *.mp4», и т.к. . и .. не *.mp4, то у него и так будет ВР.

emulek
()
Ответ на: комментарий от emulek

Вы правы. А `ls` я использовал (во всяком случае пытался) только потому, что там были только файлы mp4 без директорий.

qzxcvbnm
() автор топика
Ответ на: комментарий от qzxcvbnm

ну если уж вам так присралось именно ls, то можно так:

$ while read f; do echo "'$f'"; done < <(ls)
'a a'
'b b'

как видите это работает с пробелами, но к сожалению всё равно ломается на файлах, в имени которых есть перевод строки. Т.ч. лучше юзайте find.

emulek
()
Ответ на: комментарий от emulek

PS: и да, помните, что в команде

`ls *.mp3`

это ваше *.mp3 раскрывает совсем не ls, а сам bash. Т.е. ls уже получает список имён файлов, и печатает имена файлов. С тем же успехом можно написать

`echo *.mp3`

emulek
()
Ответ на: комментарий от teod0r

где можно почитать про подобные конструкции?

гугли ABS (есть пара переводов на русский)

В данном случае <(ls) это на самом деле просто имя файла. А сам файл представляет собой /dev/stdout от команды ls. Для команды read (в самом начале цикла) он направляется прямо в /dev/stdin. (каждый процесс имеет свой особенный /dev/std*).

Можно было-бы конечно написать ls|while read...done, но так выполняются две команды СРАЗУ, а это не очень хорошо(особенно если что-то пойдёт не так).

emulek
()

Елки палки, ТС, не слушай этих велосипедчиков - этот вариант самый правильный, можешь перечитать кучу мануалов по bash. Несогласным просьба аргументировать а не постить лапшу с тремя пайпами.

alozovskoy ★★★★★
()
Ответ на: комментарий от alozovskoy

Большое спасибо. Я его и использовал.

qzxcvbnm
() автор топика
Ответ на: комментарий от alozovskoy

1. почитай здесь: Вопрос по shell (комментарий)

2. способ с * не всегда удобен, т.к. практически не управляем. Ну и возможностей глоббинга не всегда достаточно.

3. я надеюсь, про «три пайпа» это не про меня?

emulek
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.