Как я могу заставить 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 работать с такими именами файлов?

Ответ 1

Вы можете изменить символ разделителя на новую строку ('\n') для xargs с параметром -d следующим образом:

ls *mp3 | xargs -d '\n' mplayer

Ответ 2

Утилита 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)"

Ответ 3

Попробуйте

find . -name \*.mp3 -print0 | xargs -0 mplayer

вместо

ls | grep mp3 

Ответ 4

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, прямое редактирование, возможно, не подходит в любом случае.

Ответ 5

find . -name 'Lemon*.mp3' -print0 | xargs -­0 -i mplayer '{}' 

Это помогло в моем случае удалить разные файлы с пробелами. Он также должен работать с mplayer. Необходимым трюком являются цитаты. (Testet on linux xubuntu 14.04)

Ответ 6

ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}

Обратите внимание, что в команде выше xargs будет вызывать mplayer заново для каждого файла. Это может быть нежелательным для mplayer, но может быть хорошо для других целей.

Ответ 7

Это зависит от (а) того, насколько вы привязаны к числу 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 вместо новой строки... и я не уверен, что они есть.

Первый, безусловно, самый простой для конкретного случая; однако он не может обобщать, чтобы охватить ваши другие сценарии, которые вы еще не указали.

Ответ 8

Учитывая конкретное название этого сообщения, здесь мое предложение:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'

Идея состоит в том, чтобы преобразовать пробелы в любой уникальный символ, например '<', а затем изменить это на '\', обратную косую черту, за которой следует пробел. Затем вы можете передать это в любую команду, например:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo

Ключ здесь лежит в командах 'tr' и 'sed'; и вы можете использовать любой символ, кроме '<', например '?' или даже символ табуляции.

Ответ 9

Я знаю, что я не отвечаю на вопрос 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.

Ответ 10

В macOS 10.12.x, если у вас есть пробелы в именах файлов или подкаталогах, вы можете использовать следующее:

find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l

Ответ 11

Это старый пост, но альтернативные решения могут быть полезны...

Вы также можете добавить нулевой символ в конец своих строк с помощью Perl, а затем использовать опцию -0 в xargs. В отличие от xargs -d '\n' (в одобренном ответе) - это работает везде, включая OSX.

Например, чтобы рекурсивно перечислить (выполнить, переместить и т.д.) jpg-изображения, которые могут содержать пробелы или другие смешные символы, я бы использовал:

find . | grep \.jpg | perl -ne 'chop; print "$_\0"' | xargs -0 ls

(Примечание. Для фильтрации я предпочитаю синтаксис "легче" для запоминания "grep для аргументов" find's "--name.)