Ответ 1
Вы можете изменить символ разделителя на новую строку ('\n') для xargs с параметром -d следующим образом:
ls *mp3 | xargs -d '\n' mplayer
$ 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 работать с такими именами файлов?
Вы можете изменить символ разделителя на новую строку ('\n') для xargs с параметром -d следующим образом:
ls *mp3 | xargs -d '\n' mplayer
Утилита xargs считывает строки, табуляции, новые строки и строки с разделителями по умолчанию из стандартного ввода и выполняет утилиту со строками в качестве аргументов.
Вы хотите избежать использования пространства в качестве разделителя. Это можно сделать, изменив разделитель на xargs. Согласно руководству:
-0 Change xargs to expect NUL (``\0'') 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)"
Попробуйте
find . -name \*.mp3 -print0 | xargs -0 mplayer
вместо
ls | grep mp3
Dick.Guertin answer [1] предположил, что можно избежать пробелов в имени файла, является ценной альтернативой другим предлагаемым здесь решениям (например, использование нулевого символа как разделителя, а не пробела). Но это может быть проще - вам действительно не нужен уникальный персонаж. Вы можете просто добавить sed непосредственно к экранированным пространствам:
ls | grep ' ' | sed 's| |\\ |g' | xargs ...
Кроме того, grep необходим только в том случае, если вам нужны только файлы с пробелами в именах. В более общем плане (например, при обработке пакета файлов, некоторые из которых имеют пробелы, а некоторые нет), просто пропустите grep:
ls | sed 's| |\\ |g' | xargs ...
Тогда, конечно, имя файла может иметь другие пробелы, чем пробелы (например, вкладка):
ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...
Предполагается, что у вас есть команда sed, которая поддерживает -r (расширенное регулярное выражение), например, GNU sed или последние версии bsd sed (например, FreeBSD, которая изначально использовала опцию "-E" перед FreeBSD 8 и поддерживала как -r, E для совместимости через FreeBSD 11, по крайней мере). В противном случае вы можете использовать основное выражение выражения символа символа регулярного выражения и вручную вводить пробелы и символы табуляции в разделителях []
.
[1] Это, пожалуй, более уместно в качестве комментария или редактирования для этого ответа, но на данный момент у меня недостаточно репутации для комментариев и вы можете предлагать только изменения. Поскольку последние формы выше (без grep) изменяет поведение исходного ответа Dick.Guertin, прямое редактирование, возможно, не подходит в любом случае.
find . -name 'Lemon*.mp3' -print0 | xargs -0 -i mplayer '{}'
Это помогло в моем случае удалить разные файлы с пробелами. Он также должен работать с mplayer. Необходимым трюком являются цитаты. (Testet on 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 вместо новой строки... и я не уверен, что они есть.
Первый, безусловно, самый простой для конкретного случая; однако он не может обобщать, чтобы охватить ваши другие сценарии, которые вы еще не указали.
Учитывая конкретное название этого сообщения, здесь мое предложение:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'
Идея состоит в том, чтобы преобразовать пробелы в любой уникальный символ, например '<', а затем изменить это на '\', обратную косую черту, за которой следует пробел. Затем вы можете передать это в любую команду, например:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo
Ключ здесь лежит в командах 'tr' и 'sed'; и вы можете использовать любой символ, кроме '<', например '?' или даже символ табуляции.
Я знаю, что я не отвечаю на вопрос xargs
напрямую, но стоит упомянуть опцию find
-exec
.
Учитывая следующую файловую систему:
[[email protected] bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush
0 directories, 4 files
Команда find может быть использована для обработки пространства в Dream Theater и King X. Таким образом, чтобы найти барабанщиков каждой группы, используя grep:
[[email protected]]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King 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 X" "bands/Megadeth"
и find bands/ -type f -exec grep Drums {} \;
приведет к:
grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King X"
grep Drums "bands/Megadeth"
В случае grep
это имеет побочный эффект либо печати имени файла, либо нет.
[[email protected] bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren
[[email protected] bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
Конечно, grep
опции -h
и -h
будут определять, будет ли отображаться имя файла независимо от того, как вызывается grep
.
xargs
xargs
также может контролировать, как файлы пользователя находятся в командной строке.
xargs
по умолчанию группирует все аргументы в одну строку. Чтобы сделать то же самое, что -exec \;
использует xargs -l
. Обратите внимание, что параметр -t
сообщает xargs
распечатать команду перед ее выполнением.
[[email protected] 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 X
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth
Drums:Dirk Verbeuren
Посмотрите, что параметр -l
указывает xargs выполнять grep для каждого имени файла.
В отличие от опции по умолчанию (т.е. нет -l
):
[[email protected] bokeh]# find ./bands -type f | xargs -d '\n' -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King X ./bands/Megadeth
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
xargs
имеет лучший контроль над количеством файлов в командной строке. Дайте опции -l
максимальное количество файлов для каждой команды.
[[email protected] 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 X ./bands/Megadeth
./bands/King X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[[email protected] bokeh]#
Посмотрите, что grep
был выполнен с двумя именами файлов из-за -l2
.
В macOS 10.12.x, если у вас есть пробелы в именах файлов или подкаталогах, вы можете использовать следующее:
find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l
Это старый пост, но альтернативные решения могут быть полезны...
Вы также можете добавить нулевой символ в конец своих строк с помощью Perl, а затем использовать опцию -0 в xargs. В отличие от xargs -d '\n' (в одобренном ответе) - это работает везде, включая OSX.
Например, чтобы рекурсивно перечислить (выполнить, переместить и т.д.) jpg-изображения, которые могут содержать пробелы или другие смешные символы, я бы использовал:
find . | grep \.jpg | perl -ne 'chop; print "$_\0"' | xargs -0 ls
(Примечание. Для фильтрации я предпочитаю синтаксис "легче" для запоминания "grep для аргументов" find's "--name.)