Я хочу сделать что-нибудь несколько раз в списке файлов. У файлов в вопросах есть пробелы в их именах:
david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha
-rw-rw-r-- 1 david david 0 Mai 8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (copy)
Теперь я хочу описать каждый из этих файлов:
david@david: echo '
for file in $(ls)
do
stat $file
done' | bash
(я использую echo и pipe для написания нескольких -line команды.)
Когда я это делаю, он корректно работает с теми файлами, в именах которых нет пробелов. Но остальные ...
stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory
Изменение $(ls)
на "$(ls)"
или $file
на "$file"
не работает. Что я могу сделать?
Редактировать:
echo '
for files in *
do
stat "$files"
done' | bash
делает свое дело! Поскольку я новичок в bash, я хочу, чтобы все было как можно проще - поэтому ничего не пытаясь избежать пробелов, или использовать xargs
или решение с read -r
, хотя они решают проблему.
Как некоторые спрашивают: да, использовать это вместо stat *
странно. Но я просто хотел найти общий способ применения одной и той же команды к группе имен файлов в bash, используя цикл for. Таким образом, stat
может обозначать gzip
, gpg
или rm
.
Несколько заключают в кавычки из эти echo '
, усложняет вещь.
можно просто использовать:
for f in *; do stat -- "$f"; done
, Но также и
stat -- *
... и если Вы хотите собрать файлы и затем применить команду (почему?) можно пойти с (но быть осторожными с файлом, содержащим новые строки... (1))
for f in *; do echo "$f"; done | xargs stat --
... и если Вы хотите скрытые файлы также, просто использование * .*
как шаблон, но тогда помните, что .
и ..
будет в наборе .
Как в стороне, Вы не должны анализировать ls
, производит .
(1), но если у Вас есть имена файлов с новыми строками, Вы несколько заслуживаете его...;-)
На ноте стороны: можно разделять долго / сложные команды по нескольким строкам путем добавления, что пространство, сопровождаемое обратной косой чертой и ударом, Входит каждый раз, Вы хотите начать писать в новую строку, вместо того, чтобы разветвить несколько процессов при помощи echo [...] | bash
; также необходимо включить $file
в двойных кавычках, для предотвращения stat
от повреждения в случае имен файлов, содержащих пробелы:
for file in $(ls); \
do \
stat "$file"; \
done
Проблема - это $(ls)
расширяется до списка имен файлов, содержащих пробелы, и то же произойдет также с "$(ls)"
.
Даже решая эту проблему, этот метод все еще повредится на именах файлов, содержащих обратные косые черты и на именах файлов, содержащих новые строки (как указано terdon).
Решение для обеих проблем состояло бы в том, чтобы передать вывод по каналу find
к a while
выполнение цикла read -r
так, чтобы, при каждом повторении, read -r
сохранит одну строку find
вывод в $file
:
find . -maxdepth 1 -type f | while read -r file; do \
stat "$file"; \
done
Используйте старое доброе find
, работы со скрытыми файлами, новыми строками и пробелами.
find . -print0 | xargs -I {} -0 stat {}
или любой другой вместо stat
find . -print0 | xargs -I {} -0 file {}
find . -print0 | xargs -I {} -0 cat {}
Как парень R, я уже нашел обходное решение в R:
filenames <- dir(); # reads file names into an array.
# It works also recursively
# with dir(recursive=TRUE)
for (i in 1:length(filenames)) {
system( # calls a system function
paste( # join stat and the file name
"stat",
filenames[i]
)
)
}
я знаю, это безумно. Я желаю, чтобы вывод ls
было бы легче проанализировать... R может иметь дело с пробелами, потому что dir () возвращает заключенное в кавычки символьное значение. Что-либо между кавычками - тогда имя правильного файла с пробелами.
Я столкнулся с другими экземплярами пробельных проблем в для циклов, таким образом, следующее (imo более устойчивый) команда - то, что я обычно использую. Это также соответствует приятно каналам.
$ ls | while read line; do stat "$line"; done;
Вы могли объединить это с grep
или использование find
вместо этого:
$ find ./ -maxdepth 2 | grep '^\./[/a-z]+ | while read line; do stat "$line"; done;
Этот ответ будет решать проблему парсинга ls
и заботиться о клавишах Backspace и новых строках
Попытка это, это решит Вашу проблему с помощью Внутреннего Разделителя полей IFS.
IFS="\n" for f in $(ls); do stat "$f"; done
, Но также и можно легко решить его без потребности проанализировать вывод ls с помощью
for f in *; do stat "$f"; done
Вместо этого можно переименовать файлы, заменяющие пространство некоторым другим символом, такие как подчеркивание, таким образом, Вы избавляетесь от этой проблемы:
, Чтобы сделать это легко выполняет команду:
for file in * ; do mv "$f" "${f// /_}" ; done