Сделать 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)"

попробовать

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

вместо

ls | grep mp3 

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.)