Сделать xargs обрабатывать имена файлов, содержащие пробелы
$ ls *mp3 | xargs mplayer
Playing Lemon.
File not found: 'Lemon'
Playing Tree.mp3.
File not found: 'Tree.mp3'
Exiting... (End of file)
моя команда терпит неудачу, потому что файл " Lemon Tree.mp3 " содержит пробелы, поэтому xargs считает, что это два файла. Могу ли я заставить find + xargs работать с такими именами файлов?
12 ответов
на xargs
команда принимает символы пробела (вкладки, пробелы, новые строки) в качестве разделителей. Его можно сузить только для новых символов строки ('\n') с такой:
ls *.mp3 | xargs -d '\n' mplayer
он работает только с GNU xargs. Для систем BSD используйте такой:
ls *.mp3 | xargs -0 mplayer
этот метод проще и работает также с GNU xargs.
утилита xargs считывает строки, разделенные пробелом, вкладкой, новой строкой и концом файла из стандартного ввода и выполняет утилиту со строками в качестве аргументов.
вы хотите избежать использования пространства в качестве разделителя. Это можно сделать, изменив разделитель для xargs. Согласно инструкции:
-0 Change xargs to expect NUL (``'') characters as separators, instead of spaces and newlines. This is expected to be used in concert with the -print0 function in find(1).
, например:
find . -name "*.mp3" -print0 | xargs -0 mplayer
чтобы ответить на вопрос о воспроизведении каждого седьмого mp3; проще запустить
mplayer "$(ls | grep mp3 | sed -n 7p)"
xargs на MacOS не имеет опции-d, поэтому это решение использует -0 вместо этого.
получите ls для вывода одного файла на строку, затем переведите новые строки в нули и скажите xargs использовать нули в качестве разделителя:
ls -1 *mp3 | tr "\n" "" | xargs -0 mplayer
Дик.Ответ гертина [1] предположил, что можно избежать пробелов в имени файла является ценной альтернативой другим предложенным здесь решениям (например, использование нулевого символа в качестве разделителя, а не пробелов). Но это может быть проще - вам действительно не нужен уникальный персонаж. Вы можете просто sed добавить экранированные пробелы напрямую:
ls | grep ' ' | sed 's| |\ |g' | xargs ...
кроме того, grep необходим только в том случае, если вы только файлы с пробелами в именах. Больше в общем случае (например, при обработке пакета файлов, некоторые из которых имеют пробелы, некоторые нет), просто пропустите grep:
ls | sed 's| |\ |g' | xargs ...
тогда, конечно, имя файла может иметь другие пробелы, чем пробелы (например, вкладка):
ls | sed -r 's|[[:blank:]]|\|g' | xargs ...
это предполагает, что у вас есть sed, который поддерживает-r (расширенное регулярное выражение), такое как GNU sed или последние версии BSD sed (например, FreeBSD, которая первоначально написала опцию "- E " перед FreeBSD 8 и поддерживает оба-r & - E для совместимости через FreeBSD 11 на наименьший.) В противном случае вы можете использовать базовое выражение скобки класса символов regex и вручную ввести символы пробела и вкладки в []
разделители.
[1] это, возможно, более уместно в качестве комментария или редактирования к этому ответу, но на данный момент у меня недостаточно репутации для комментариев и я могу только предлагать изменения. Поскольку последние формы выше (без grep) изменяет поведение Дика.Оригинальный ответ Guertin, прямое редактирование, возможно, не подходит в любом случае.
find . -name 'Lemon*.mp3' -print0 | xargs -0 -i mplayer '{}'
это помогло в моем случае удалить разные файлы с пробелами. Он должен работать с mplayer. Необходимый трюк-кавычки. (Проверено на Linux Xubuntu в 14.04.)
ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}
обратите внимание, что в приведенной выше команде, xargs
будем называть mplayer
заново для каждого файла. Это может быть нежелательно для mplayer
, но может быть хорошо для других целей.
это зависит от (А) того, насколько вы привязаны к номеру 7, в отличие, скажем, от лимонов, и (Б) содержит ли какое-либо из ваших имен файлов новые строки (и готовы ли вы переименовать их, если они это сделают).
есть много способов справиться с этим, но некоторые из них:
mplayer Lemon*.mp3
find . -name 'Lemon*.mp3' -exec mplayer {} ';'
i=0
for mp3 in *.mp3
do
i=$((i+1))
[ $i = 7 ] && mplayer "$mp3"
done
for mp3 in *.mp3
do
case "$mp3" in
(Lemon*) mplayer "$mp3";;
esac
done
i=0
find . -name *.mp3 |
while read mp3
do
i=$((i+1))
[ $i = 7 ] && mplayer "$mp3"
done
на read
цикл не работает, если имена файлов содержат новые строки; другие работают правильно даже с новыми строками в именах (не говоря уже о пробелах). За мои деньги, если у вас есть имена файлов, содержащих новая строка, вы должны переименовать файл без новой строки. Использование двойных кавычек вокруг имени файла является ключом к правильной работе циклов.
если у вас есть GNU find
и GNU xargs
(или FreeBSD (*BSD?), или Mac OS X), вы также можете использовать -print0
и -0
"параметры", как в:
find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer
это работает независимо от содержимого имени (только два символа, которые не могут отображаться в имени файла, - косая черта и NUL, и косая черта не вызывает проблем в файле путь, поэтому использование NUL в качестве разделителя имен охватывает все). Однако, если вам нужно отфильтровать первые 6 записей, вам нужна программа, которая обрабатывает "строки", заканчивающиеся NUL вместо newline...и я не уверен, что есть.
первый, безусловно, самый простой для конкретного случая; однако, он не может обобщать, чтобы охватить другие сценарии, которые вы еще не перечислили.
я знаю, что не отвечаю на xargs
вопрос но это стоит упомянуть find
' s .
учитывая следующую файловую систему:
[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush
0 directories, 4 files
команда find может быть сделана для обработки пространства в Dream Theater и King'S X. Итак, чтобы найти барабанщиков каждой группы с помощью grep:
[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
на -exec
опции {}
обозначает имя файла, включая путь. Обратите внимание, что вам не нужно избегать его или в кавычках.
разницу между -exec
с ограничителями (+
и \;
), что +
группирует столько имен файлов, сколько он может в одной командной строке. Тогда как \;
выполнит команду для каждого имени файла.
и find bands/ -type f -exec grep Drums {} +
в результате:
grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
и find bands/ -type f -exec grep Drums {} \;
в результате:
grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"
в случае grep
это имеет побочный эффект печати имени файла или не.
[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren
[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
конечно, grep
'параметры с -h
и -H
будет контролировать, печатается ли имя файла независимо от того, как grep
называется.
xargs
xargs
также можно контролировать, как файлы man находятся в командной строке.
xargs
по умолчанию группирует все аргументы в одну строку. Для того, чтобы сделать то же самое, что -exec \;
использовать xargs -l
. Обратите внимание, что говорит xargs
в распечатайте команду перед ее выполнением.
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater
Drums:Mike Mangini
grep Drums ./bands/Rush
Drums: Neil Peart
grep Drums ./bands/King's X
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth
Drums:Dirk Verbeuren
видно, что -l
опция сообщает xargs выполнить grep для каждого имени файла.
и по умолчанию (т. е. без ):
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
xargs
имеет лучший контроль того, сколько файлов может быть в командной строке. Дайте -l
опция максимальное количество файлов на команду.
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]#
видно, что grep
был выполнен с двумя именами файлов из-за -l2
.
на macOS 10.12.x (Sierra), если у вас есть пробелы в именах файлов или подкаталогах, вы можете использовать следующее:
find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l
учитывая конкретное название этого поста, вот мое предложение:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\ |g'
идея состоит в том, чтобы преобразовать пробелы в любой уникальный символ, например"
ls | grep ' ' | tr ' ' '<' | sed 's|<|\ |g' | xargs -L1 GetFileInfo
ключ здесь лежит в командах " tr " и "sed"; и вы можете использовать любой символ, кроме"
альтернативные решения могут быть полезны...
вы также можете добавить нулевой символ в конец строки, используя Perl, а затем использовать -0
опция в xargs. В отличие от xargs-d ' \n '(в утвержденном ответе) - это работает везде, включая OS X.
например, рекурсивно перечислить (выполнить, переместить и т. д.) JPEG-изображения, которые могут содержать пробелы или другие забавные символы - я бы использовал:
find . | grep \.jpg | perl -ne 'chop; print "$_"' | xargs -0 ls
(Примечание: для фильтрации я предпочитаю более простой в запоминании "| grep "синтаксис для Аргументов" find " --name.)